Skip to content
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

NFC: clean up inlining test code #42804

Merged
merged 1 commit into from
Oct 26, 2021
Merged
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
215 changes: 121 additions & 94 deletions test/compiler/inline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -380,15 +380,23 @@ using Base.Experimental: @opaque
f_oc_getfield(x) = (@opaque ()->x)()
@test fully_eliminated(f_oc_getfield, Tuple{Int})

# 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
const EMPTY_SPTYPES = Core.Compiler.EMPTY_SLOTTYPES

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
argextype(x, src, EMPTY_SPTYPES) === typeof(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)

@testset "@inline/@noinline annotation before definition" begin
M = Module()
Expand All @@ -413,25 +421,25 @@ code_typed1(args...; kwargs...) = (first(only(code_typed(args...; kwargs...)))::
def_noinline_noconflict(x) = _def_noinline_noconflict(x)
end

let code = code_typed1(M.def_inline, (Int,))
@test all(code) do x
!isinvoke(x, :_def_inline)
let code = get_code(M.def_inline, (Int,))
@test all(code) do @nospecialize x
!isinvoke(:_def_inline, x)
end
end
let code = code_typed1(M.def_noinline, (Int,))
@test any(code) do x
isinvoke(x, :_def_noinline)
let code = get_code(M.def_noinline, (Int,))
@test any(code) do @nospecialize x
isinvoke(:_def_noinline, x)
end
end
# test that they don't conflict with other "before-definition" macros
let code = code_typed1(M.def_inline_noconflict, (Int,))
@test all(code) do x
!isinvoke(x, :_def_inline_noconflict)
let code = get_code(M.def_inline_noconflict, (Int,))
@test all(code) do @nospecialize x
!isinvoke(:_def_inline_noconflict, x)
end
end
let code = code_typed1(M.def_noinline_noconflict, (Int,))
@test any(code) do x
isinvoke(x, :_def_noinline_noconflict)
let code = get_code(M.def_noinline_noconflict, (Int,))
@test any(code) do @nospecialize x
isinvoke(:_def_noinline_noconflict, x)
end
end
end
Expand Down Expand Up @@ -470,29 +478,33 @@ end
end
end

let code = code_typed1(M.body_inline, (Int,))
@test all(code) do x
!isinvoke(x, :_body_inline)
let code = get_code(M.body_inline, (Int,))
@test all(code) do @nospecialize x
!isinvoke(:_body_inline, x)
end
end
let code = code_typed1(M.body_noinline, (Int,))
@test any(code) do x
isinvoke(x, :_body_noinline)
let code = get_code(M.body_noinline, (Int,))
@test any(code) do @nospecialize x
isinvoke(:_body_noinline, x)
end
end
# test annotations for `do` blocks
let code = code_typed1(M.do_inline, (Int,))
let code = get_code(M.do_inline, (Int,))
# what we test here is that both `simple_caller` and the anonymous function that the
# `do` block creates should inlined away, and as a result there is only the unresolved call
@test all(code) do x
!isinvoke(x, :simple_caller) &&
!isinvoke(x, mi->startswith(string(mi.def.name), '#'))
@test all(code) do @nospecialize x
!isinvoke(:simple_caller, x) &&
!isinvoke(x) do mi
startswith(string(mi.def.name), '#')
end
end
end
let code = code_typed1(M.do_noinline, (Int,))
let code = get_code(M.do_noinline, (Int,))
# the anonymous function that the `do` block created shouldn't be inlined here
@test any(code) do x
isinvoke(x, mi->startswith(string(mi.def.name), '#'))
@test any(code) do @nospecialize x
isinvoke(x) do mi
startswith(string(mi.def.name), '#')
end
end
end
end
Expand Down Expand Up @@ -520,14 +532,14 @@ end
# test callsite annotations for constant-prop'ed calls

@noinline Base.@constprop :aggressive noinlined_constprop_explicit(a) = a+g
force_inline_constprop_explicit() = @inline noinlined_constprop_explicit(0)
force_inline_constprop_explicit() = @inline noinlined_constprop_explicit(0)
Base.@constprop :aggressive noinlined_constprop_implicit(a) = a+g
force_inline_constprop_implicit() = @inline noinlined_constprop_implicit(0)
force_inline_constprop_implicit() = @inline noinlined_constprop_implicit(0)

@inline Base.@constprop :aggressive inlined_constprop_explicit(a) = a+g
force_noinline_constprop_explicit() = @noinline inlined_constprop_explicit(0)
force_noinline_constprop_explicit() = @noinline inlined_constprop_explicit(0)
@inline Base.@constprop :aggressive inlined_constprop_implicit(a) = a+g
force_noinline_constprop_implicit() = @noinline inlined_constprop_implicit(0)
force_noinline_constprop_implicit() = @noinline inlined_constprop_implicit(0)

@noinline notinlined(a) = a
function nested(a0, b0)
Expand All @@ -539,51 +551,75 @@ end
end
end

let code = code_typed1(M.force_inline_explicit, (Int,))
@test all(x->!isinvoke(x, :noinlined_explicit), code)
let code = get_code(M.force_inline_explicit, (Int,))
@test all(code) do @nospecialize x
!isinvoke(:noinlined_explicit, x)
end
end
let code = code_typed1(M.force_inline_block_explicit, (Int,))
@test all(code) do x
!isinvoke(x, :noinlined_explicit) &&
!isinvoke(x, :(+))
let code = get_code(M.force_inline_block_explicit, (Int,))
@test all(code) do @nospecialize x
!isinvoke(:noinlined_explicit, x) &&
!isinvoke(:(+), x)
end
end
let code = code_typed1(M.force_inline_implicit, (Int,))
@test all(x->!isinvoke(x, :noinlined_implicit), code)
let code = get_code(M.force_inline_implicit, (Int,))
@test all(code) do @nospecialize x
!isinvoke(:noinlined_implicit, x)
end
end
let code = code_typed1(M.force_inline_block_implicit, (Int,))
@test all(x->!isinvoke(x, :noinlined_explicit), code)
let code = get_code(M.force_inline_block_implicit, (Int,))
@test all(code) do @nospecialize x
!isinvoke(:noinlined_explicit, x)
end
end

let code = code_typed1(M.force_noinline_explicit, (Int,))
@test any(x->isinvoke(x, :inlined_explicit), code)
let code = get_code(M.force_noinline_explicit, (Int,))
@test any(code) do @nospecialize x
isinvoke(:inlined_explicit, x)
end
end
let code = code_typed1(M.force_noinline_block_explicit, (Int,))
@test count(x->isinvoke(x, :inlined_explicit), code) == 2
let code = get_code(M.force_noinline_block_explicit, (Int,))
@test count(code) do @nospecialize x
isinvoke(:inlined_explicit, x)
end == 2
end
let code = code_typed1(M.force_noinline_implicit, (Int,))
@test any(x->isinvoke(x, :inlined_implicit), code)
let code = get_code(M.force_noinline_implicit, (Int,))
@test any(code) do @nospecialize x
isinvoke(:inlined_implicit, x)
end
end
let code = code_typed1(M.force_noinline_block_implicit, (Int,))
@test count(x->isinvoke(x, :inlined_implicit), code) == 2
let code = get_code(M.force_noinline_block_implicit, (Int,))
@test count(code) do @nospecialize x
isinvoke(:inlined_implicit, x)
end == 2
end

let code = code_typed1(M.force_inline_constprop_explicit)
@test all(x->!isinvoke(x, :noinlined_constprop_explicit), code)
let code = get_code(M.force_inline_constprop_explicit)
@test all(code) do @nospecialize x
!isinvoke(:noinlined_constprop_explicit, x)
end
end
let code = code_typed1(M.force_inline_constprop_implicit)
@test all(x->!isinvoke(x, :noinlined_constprop_implicit), code)
let code = get_code(M.force_inline_constprop_implicit)
@test all(code) do @nospecialize x
!isinvoke(:noinlined_constprop_implicit, x)
end
end

let code = code_typed1(M.force_noinline_constprop_explicit)
@test any(x->isinvoke(x, :inlined_constprop_explicit), code)
let code = get_code(M.force_noinline_constprop_explicit)
@test any(code) do @nospecialize x
isinvoke(:inlined_constprop_explicit, x)
end
end
let code = code_typed1(M.force_noinline_constprop_implicit)
@test any(x->isinvoke(x, :inlined_constprop_implicit), code)
let code = get_code(M.force_noinline_constprop_implicit)
@test any(code) do @nospecialize x
isinvoke(:inlined_constprop_implicit, x)
end
end

let code = code_typed1(M.nested, (Int,Int))
@test count(x->isinvoke(x, :notinlined), code) == 1
let code = get_code(M.nested, (Int,Int))
@test count(code) do @nospecialize x
isinvoke(:notinlined, x)
end == 1
end
end

Expand All @@ -604,10 +640,12 @@ let code = @eval Module() begin
end
end

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

@test !any(x->isinvoke(x, :setproperty!), code)
@test !any(code) do @nospecialize x
isinvoke(:setproperty!, x)
end
end

# Issue #41299 - inlining deletes error check in :>
Expand All @@ -624,10 +662,12 @@ end
@noinline f42078(a) = sum(sincos(a))
let
ninlined = let
code = code_typed1((Int,)) do a
code = get_code((Int,)) do a
@inline f42078(a)
end
@test all(x->!isinvoke(x, :f42078), code)
@test all(code) do @nospecialize x
!isinvoke(:f42078, x)
end
length(code)
end

Expand All @@ -643,10 +683,12 @@ let
end

let # inference should re-infer `f42078(::Int)` and we should get the same code
code = code_typed1((Int,)) do a
code = get_code((Int,)) do a
@inline f42078(a)
end
@test all(x->!isinvoke(x, :f42078), code)
@test all(code) do @nospecialize x
!isinvoke(:f42078, x)
end
@test ninlined == length(code)
end
end
Expand Down Expand Up @@ -688,21 +730,16 @@ mutable struct X42754
a::Union{Nothing, Int}
b::Symbol
end
let code = code_typed1((X42754, Union{Nothing,Int})) do x, a
let src = code_typed1((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
# end up very high if we don't propagate `sym::Const(:a)`
x.a = a
x
end
@test all(code) do @nospecialize(x)
isinvoke(x, :setproperty!) && return false
if Meta.isexpr(x, :call)
f = x.args[1]
isa(f, GlobalRef) && f.name === :setproperty! && return false
end
return true
@test all(src.code) do @nospecialize x
!(isinvoke(:setproperty!, x) || iscall((src, setproperty!), x))
end
end

Expand All @@ -713,32 +750,22 @@ import Base: @constprop
@constprop :none @inline test_single_nondispatchtuple(@nospecialize(t)) =
isa(t, DataType) && t.name === Type.body.name
let
code = code_typed1((Any,)) do x
src = code_typed1((Any,)) do x
test_single_nondispatchtuple(x)
end
@test all(code) do @nospecialize(x)
isinvoke(x, :test_single_nondispatchtuple) && return false
if Meta.isexpr(x, :call)
f = x.args[1]
isa(f, GlobalRef) && f.name === :test_single_nondispatchtuple && return false
end
return true
@test all(src.code) do @nospecialize x
!(isinvoke(:test_single_nondispatchtuple, x) || iscall((src, test_single_nondispatchtuple), x))
end
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
src = code_typed1((Any,)) do x
test_single_nondispatchtuple(true, x)
end
@test all(code) do @nospecialize(x)
isinvoke(x, :test_single_nondispatchtuple) && return false
if Meta.isexpr(x, :call)
f = x.args[1]
isa(f, GlobalRef) && f.name === :test_single_nondispatchtuple && return false
end
return true
@test all(src.code) do @nospecialize(x)
!(isinvoke(:test_single_nondispatchtuple, x) || iscall((src, test_single_nondispatchtuple), x))
end
end

Expand Down