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

reflection: use CC.findall instead of _methods_by_ftype #54212

Merged
merged 2 commits into from
Apr 25, 2024
Merged
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
84 changes: 40 additions & 44 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,12 @@ function default_tt(@nospecialize(f))
end
end

function raise_match_failure(name::Symbol, @nospecialize(tt))
@noinline
sig_str = sprint(Base.show_tuple_as_call, Symbol(""), tt)
error("$name: unanalyzable call given $sig_str")
end

"""
code_typed_by_type(types::Type{<:Tuple}; ...)

Expand All @@ -1629,9 +1635,10 @@ function code_typed_by_type(@nospecialize(tt::Type);
throw(ArgumentError("'debuginfo' must be either :source or :none"))
end
tt = to_tuple_type(tt)
matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
matches === nothing && raise_match_failure(:code_typed, tt)
asts = []
for match in matches
for match in matches.matches
match = match::Core.MethodMatch
(code, ty) = Core.Compiler.typeinf_code(interp, match, optimize)
if code === nothing
Expand Down Expand Up @@ -1746,9 +1753,10 @@ function code_ircode_by_type(
(ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) &&
error("code reflection cannot be used from generated functions")
tt = to_tuple_type(tt)
matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
matches === nothing && raise_match_failure(:code_ircode, tt)
asts = []
for match in matches
for match in matches.matches
match = match::Core.MethodMatch
(code, ty) = Core.Compiler.typeinf_ircode(interp, match, optimize_until)
if code === nothing
Expand All @@ -1774,6 +1782,12 @@ function _builtin_effects(interp::Core.Compiler.AbstractInterpreter,
return Core.Compiler.builtin_effects(Core.Compiler.typeinf_lattice(interp), f, argtypes, rt)
end

function _builtin_exception_type(interp::Core.Compiler.AbstractInterpreter,
@nospecialize(f::Core.Builtin), @nospecialize(types))
effects = _builtin_effects(interp, f, types)
return Core.Compiler.is_nothrow(effects) ? Union{} : Any
end

check_generated_context(world::UInt) =
(ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) &&
error("code reflection cannot be used from generated functions")
Expand Down Expand Up @@ -1832,15 +1846,14 @@ function return_types(@nospecialize(f), @nospecialize(types=default_tt(f));
if isa(f, Core.OpaqueClosure)
_, rt = only(code_typed_opaque_closure(f, types))
return Any[rt]
elseif isa(f, Core.Builtin)
return Any[_builtin_return_type(interp, f, types)]
end
if isa(f, Core.Builtin)
rt = _builtin_return_type(interp, f, types)
return Any[rt]
end
rts = Any[]
tt = signature_type(f, types)
matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector
for match in matches
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
matches === nothing && raise_match_failure(:return_types, tt)
rts = Any[]
for match in matches.matches
ty = Core.Compiler.typeinf_type(interp, match::Core.MethodMatch)
push!(rts, something(ty, Any))
end
Expand Down Expand Up @@ -1900,17 +1913,12 @@ function infer_return_type(@nospecialize(f), @nospecialize(types=default_tt(f));
check_generated_context(world)
if isa(f, Core.OpaqueClosure)
return last(only(code_typed_opaque_closure(f, types)))
end
if isa(f, Core.Builtin)
elseif isa(f, Core.Builtin)
return _builtin_return_type(interp, f, types)
end
tt = signature_type(f, types)
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
if matches === nothing
# unanalyzable call, i.e. the interpreter world might be newer than the world where
# the `f` is defined, return the unknown return type
return Any
end
matches === nothing && raise_match_failure(:infer_return_type, tt)
rt = Union{}
for match in matches.matches
ty = Core.Compiler.typeinf_type(interp, match::Core.MethodMatch)
Expand Down Expand Up @@ -1975,18 +1983,15 @@ function infer_exception_types(@nospecialize(f), @nospecialize(types=default_tt(
check_generated_context(world)
if isa(f, Core.OpaqueClosure)
return Any[Any] # TODO
elseif isa(f, Core.Builtin)
return Any[_builtin_exception_type(interp, f, types)]
end
if isa(f, Core.Builtin)
effects = _builtin_effects(interp, f, types)
exct = Core.Compiler.is_nothrow(effects) ? Union{} : Any
return Any[exct]
end
excts = Any[]
tt = signature_type(f, types)
matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector
for match in matches
match = match::Core.MethodMatch
frame = Core.Compiler.typeinf_frame(interp, match, #=run_optimizer=#false)
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
matches === nothing && raise_match_failure(:infer_exception_types, tt)
excts = Any[]
for match in matches.matches
frame = Core.Compiler.typeinf_frame(interp, match::Core.MethodMatch, #=run_optimizer=#false)
if frame === nothing
exct = Any
else
Expand Down Expand Up @@ -2057,18 +2062,12 @@ function infer_exception_type(@nospecialize(f), @nospecialize(types=default_tt(f
check_generated_context(world)
if isa(f, Core.OpaqueClosure)
return Any # TODO
end
if isa(f, Core.Builtin)
effects = _builtin_effects(interp, f, types)
return Core.Compiler.is_nothrow(effects) ? Union{} : Any
elseif isa(f, Core.Builtin)
return _builtin_exception_type(interp, f, types)
end
tt = signature_type(f, types)
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
if matches === nothing
# unanalyzable call, i.e. the interpreter world might be newer than the world where
# the `f` is defined, return the unknown exception type
return Any
end
matches === nothing && raise_match_failure(:infer_exception_type, tt)
exct = Union{}
if _may_throw_methoderror(matches)
# account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature.
Expand Down Expand Up @@ -2149,11 +2148,7 @@ function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f));
end
tt = signature_type(f, types)
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
if matches === nothing
# unanalyzable call, i.e. the interpreter world might be newer than the world where
# the `f` is defined, return the unknown effects
return Core.Compiler.Effects()
end
matches === nothing && raise_match_failure(:infer_effects, tt)
effects = Core.Compiler.EFFECTS_TOTAL
if _may_throw_methoderror(matches)
# account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature.
Expand Down Expand Up @@ -2184,10 +2179,11 @@ function print_statement_costs(io::IO, @nospecialize(tt::Type);
interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world))
tt = to_tuple_type(tt)
world == typemax(UInt) && error("code reflection cannot be used from generated functions")
matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
matches === nothing && raise_match_failure(:print_statement_costs, tt)
params = Core.Compiler.OptimizationParams(interp)
cst = Int[]
for match in matches
for match in matches.matches
match = match::Core.MethodMatch
println(io, match.method)
(code, ty) = Core.Compiler.typeinf_code(interp, match, true)
Expand Down
6 changes: 4 additions & 2 deletions stdlib/InteractiveUtils/src/codeview.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ function code_warntype(io::IO, @nospecialize(f), @nospecialize(tt=Base.default_t
print_warntype_codeinfo(io, Base.code_typed_opaque_closure(f, tt)[1]..., nargs; lineprinter)
return nothing
end
matches = Base._methods_by_ftype(Base.signature_type(f, tt), #=lim=#-1, world)::Vector
for match in matches
tt = Base.signature_type(f, tt)
matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp))
matches === nothing && Base.raise_match_failure(:code_warntype, tt)
for match in matches.matches
match = match::Core.MethodMatch
(src, rettype) = Core.Compiler.typeinf_code(interp, match, optimize)
mi = Core.Compiler.specialize_method(match)
Expand Down
31 changes: 17 additions & 14 deletions test/compiler/AbstractInterpreter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ function CC.add_remark!(interp::MTOverlayInterp, ::CC.InferenceState, remark)
return nothing
end

struct StrangeSinError end
strangesin(x) = sin(x)
@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x)
@overlay OverlayedMT strangesin(x::Float64) =
iszero(x) ? throw(StrangeSinError()) : x < 0 ? nothing : cos(x)

# inference should use the overlayed method table
@test Base.return_types((Float64,); interp=MTOverlayInterp()) do x
Expand All @@ -52,6 +54,11 @@ end |> only === Union{Float64,Nothing}
@test Base.return_types((Any,); interp=MTOverlayInterp()) do x
@invoke strangesin(x::Float64)
end |> only === Union{Float64,Nothing}
@test only(Base.return_types(strangesin, (Float64,); interp=MTOverlayInterp())) === Union{Float64,Nothing}
@test Base.infer_exception_type(strangesin, (Float64,); interp=MTOverlayInterp()) === Union{StrangeSinError,DomainError}
@test only(Base.infer_exception_types(strangesin, (Float64,); interp=MTOverlayInterp())) === Union{StrangeSinError,DomainError}
@test last(only(code_typed(strangesin, (Float64,); interp=MTOverlayInterp()))) === Union{Float64,Nothing}
@test last(only(Base.code_ircode(strangesin, (Float64,); interp=MTOverlayInterp()))) === Union{Float64,Nothing}

# effect analysis should figure out that the overlayed method is used
@test Base.infer_effects((Float64,); interp=MTOverlayInterp()) do x
Expand Down Expand Up @@ -366,12 +373,15 @@ let src = code_typed1((Float64,Float64,Float64)) do x, y, z
@test count(iscall((src, inlined_usually)), src.code) == 0
end
let NoinlineModule = Module()
OtherModule = Module()
main_func(x, y, z) = inlined_usually(x, y, z)
@eval NoinlineModule noinline_func(x, y, z) = $inlined_usually(x, y, z)
@eval OtherModule other_func(x, y, z) = $inlined_usually(x, y, z)

interp = NoinlineInterpreter(Set((NoinlineModule,)))

# this anonymous function's context is Main -- it should be inlined as usual
let src = code_typed1((Float64,Float64,Float64); interp) do x, y, z
inlined_usually(x, y, z)
end
let src = code_typed1(main_func, (Float64,Float64,Float64); interp)
@test count(isinvoke(:inlined_usually), src.code) == 0
@test count(iscall((src, inlined_usually)), src.code) == 0
end
Expand All @@ -380,26 +390,19 @@ let NoinlineModule = Module()
method = only(methods(inlined_usually, (Float64,Float64,Float64,)))
mi = CC.specialize_method(method, Tuple{typeof(inlined_usually),Float64,Float64,Float64}, Core.svec())
@test CC.haskey(CC.code_cache(interp), mi)
let src = code_typed1((Float64,Float64,Float64); interp) do x, y, z
inlined_usually(x, y, z)
end
let src = code_typed1(main_func, (Float64,Float64,Float64); interp)
@test count(isinvoke(:inlined_usually), src.code) == 0
@test count(iscall((src, inlined_usually)), src.code) == 0
end

# now the context module is `NoinlineModule` -- it should not be inlined
let src = @eval NoinlineModule $code_typed1((Float64,Float64,Float64); interp=$interp) do x, y, z
$inlined_usually(x, y, z)
end
let src = code_typed1(NoinlineModule.noinline_func, (Float64,Float64,Float64); interp)
@test count(isinvoke(:inlined_usually), src.code) == 1
@test count(iscall((src, inlined_usually)), src.code) == 0
end

# the context module is totally irrelevant -- it should be inlined as usual
OtherModule = Module()
let src = @eval OtherModule $code_typed1((Float64,Float64,Float64); interp=$interp) do x, y, z
$inlined_usually(x, y, z)
end
let src = code_typed1(OtherModule.other_func, (Float64,Float64,Float64); interp)
@test count(isinvoke(:inlined_usually), src.code) == 0
@test count(iscall((src, inlined_usually)), src.code) == 0
end
Expand Down
2 changes: 1 addition & 1 deletion test/error.jl
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ end
for name in names(mod, all=true)
isdefined(mod, name) || continue
value = getfield(mod, name)

if value isa Module
value === Main && continue
test_exceptions(value, visited)
elseif value isa Type
str = string(value)
Expand Down