Skip to content

Commit

Permalink
optimizer: fix #42840, the performance regression introduced by #42766 (
Browse files Browse the repository at this point in the history
  • Loading branch information
aviatesk committed Oct 30, 2021
1 parent 901a3a5 commit 382129f
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 13 deletions.
2 changes: 1 addition & 1 deletion base/compiler/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1392,7 +1392,7 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
ir, idx, stmt, info, sig,
state, sig.f === Core.invoke, todo) && continue
end
info = info.call
info = info.call # cascade to the non-constant handling
end

if isa(info, OpaqueClosureCallInfo)
Expand Down
58 changes: 46 additions & 12 deletions test/compiler/inline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -393,24 +393,31 @@ let f(x) = (x...,)
@test code_typed(f, Tuple{Union{Int64, CartesianIndex{1}, CartesianIndex{3}}})[1][2] == Tuple{Int64}
end

# check if `x` is a statically-resolved call of a function whose name is `sym`
isinvoke(@nospecialize(x), sym::Symbol) = isinvoke(x, mi->mi.def.name===sym)
function isinvoke(@nospecialize(x), pred)
if Meta.isexpr(x, :invoke)
return pred(x.args[1]::Core.MethodInstance)
import Core.Compiler: argextype, singleton_type

code_typed1(args...; kwargs...) = first(only(code_typed(args...; kwargs...)))::Core.CodeInfo
get_code(args...; kwargs...) = code_typed1(args...; kwargs...).code

# check if `x` is a dynamic call of a given function
function iscall((src, f)::Tuple{Core.CodeInfo,Function}, @nospecialize(x))
return iscall(x) do @nospecialize x
singleton_type(argextype(x, src, Any[])) === f
end
return false
end
code_typed1(args...; kwargs...) = (first(only(code_typed(args...; kwargs...)))::Core.CodeInfo).code
iscall(pred, @nospecialize(x)) = Meta.isexpr(x, :call) && pred(x.args[1])

# check if `x` is a statically-resolved call of a function whose name is `sym`
isinvoke(sym::Symbol, @nospecialize(x)) = isinvoke(mi->mi.def.name===sym, x)
isinvoke(pred, @nospecialize(x)) = Meta.isexpr(x, :invoke) && pred(x.args[1]::Core.MethodInstance)

# https://github.com/JuliaLang/julia/issues/42754
# inline union-split constant-prop'ed sources
# inline union-split constant-prop'ed results
mutable struct X42754
# NOTE in order to confuse `fieldtype_tfunc`, we need to have at least two fields with different types
a::Union{Nothing, Int}
b::Symbol
end
let code = code_typed1((X42754, Union{Nothing,Int})) do x, a
let code = get_code((X42754, Union{Nothing,Int})) do x, a
# this `setproperty` call would be union-split and constant-prop will happen for
# each signature: inlining would fail if we don't use constant-prop'ed source
# since the approximate inlining cost of `convert(fieldtype(X, sym), a)` would
Expand All @@ -430,12 +437,39 @@ end

import Base: @constprop

# test union-split callsite with successful and unsuccessful constant-prop' results
@constprop :aggressive @inline f42840(xs, a::Int) = xs[a] # should be successful, and inlined
@constprop :none @noinline f42840(xs::AbstractVector, a::Int) = xs[a] # should be unsuccessful, but still statically resolved
let src = code_typed1((Union{Tuple{Int,Int,Int}, Vector{Int}},)) do xs
f42840(xs, 2)
end
@test count(src.code) do @nospecialize x
iscall((src, getfield), x) # `(xs::Tuple{Int,Int,Int})[a::Const(2)]` => `getfield(xs, 2)`
end == 1
@test count(src.code) do @nospecialize x
isinvoke(:f42840, x)
end == 1
end
# a bit weird, but should handle this kind of case as well
@constprop :aggressive @noinline g42840(xs, a::Int) = xs[a] # should be successful, but only statically resolved
@constprop :none @inline g42840(xs::AbstractVector, a::Int) = xs[a] # should be unsuccessful, still inlined
let src = code_typed1((Union{Tuple{Int,Int,Int}, Vector{Int}},)) do xs
g42840(xs, 2)
end
@test count(src.code) do @nospecialize x
iscall((src, Base.arrayref), x) # `(xs::Vector{Int})[a::Const(2)]` => `Base.arrayref(true, xs, 2)`
end == 1
@test count(src.code) do @nospecialize x
isinvoke(:g42840, x)
end == 1
end

# test single, non-dispatchtuple callsite inlining

@constprop :none @inline test_single_nondispatchtuple(@nospecialize(t)) =
isa(t, DataType) && t.name === Type.body.name
let
code = code_typed1((Any,)) do x
code = get_code((Any,)) do x
test_single_nondispatchtuple(x)
end
@test all(code) do @nospecialize(x)
Expand All @@ -451,7 +485,7 @@ end
@constprop :aggressive @inline test_single_nondispatchtuple(c, @nospecialize(t)) =
c && isa(t, DataType) && t.name === Type.body.name
let
code = code_typed1((Any,)) do x
code = get_code((Any,)) do x
test_single_nondispatchtuple(true, x)
end
@test all(code) do @nospecialize(x)
Expand Down Expand Up @@ -481,7 +515,7 @@ let m = Module()
end
end

$code_typed1(setter, (Vector{Foo},))
$get_code(setter, (Vector{Foo},))
end

@test !any(x->isinvoke(x, :setproperty!), code)
Expand Down

0 comments on commit 382129f

Please sign in to comment.