From 0cd37d32a3aab946c19d44117bad20838242dfbc Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Mon, 15 Nov 2021 19:59:19 +0900 Subject: [PATCH] add some nospecialization hacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous `@nospecialize(::Type)` dispatch signatures actually disabled nospecializations, and we see following kind of dynamic dispatches: ```julia julia> using JET julia> report_opt((Vector{Any},)) do ts isbitstype(ts[1]) end ═════ 1 possible error found ═════ ┌ @ REPL[3]:2 Main.isbitstype(%1) │ runtime dispatch detected: Main.isbitstype(%1::Any) └───────────── ``` This commit also adds an overload of `emptymutable(::IdSet)`, which eliminates the dynamic dispatch below: ```julia julia> report_opt((Base.IdSet{Any}, Base.IdSet{Any}); annotate_types=true) do s1, s2 s1 ∪ s2 end ═════ 2 possible errors found ═════ ┌ @ REPL[3]:2 Main.∪(s1::Base.IdSet{Any}, s2::Base.IdSet{Any}) │┌ @ abstractset.jl:48 Base.union!(Core.tuple(Base.emptymutable(s::Base.IdSet{Any}, Base.promote_eltype(Core.tuple(s::Base.IdSet{Any})::Tuple{Base.IdSet{Any}}, sets::Tuple{Base.IdSet{Any}}...)::Type{Any})::Set{Any}, s::Base.IdSet{Any})::Tuple{Set{Any}, Base.IdSet{Any}}, sets::Tuple{Base.IdSet{Any}}...) ││┌ @ abstractset.jl:76 Base.union!(s::Set{Any}, x::Base.IdSet{Any}) │││┌ @ abstractset.jl:93 Base.sizehint!(s::Set{Any}, Base.+(Base.length(s::Set{Any})::Int64, Core.typeassert(Base.Int(Base.length(itr::Base.IdSet{Any})::Int64)::Int64, Base.Int)::Int64)::Int64) ││││┌ @ set.jl:84 Base.sizehint!(Base.getproperty(s::Set{Any}, :dict::Symbol)::Dict{Any, Nothing}, newsz::Int64) │││││┌ @ dict.jl:244 Base.rehash!(d::Dict{Any, Nothing}, newsz::Int64) ││││││┌ @ dict.jl:203 Base.hashindex(%231::Any, %20::Int64) │││││││ runtime dispatch detected: Base.hashindex(%231::Any, %20::Int64) ││││││└─────────────── │││┌ @ abstractset.jl:95 Base.push!(s::Set{Any}, %62::Any) ││││ runtime dispatch detected: Base.push!(s::Set{Any}, %62::Any) │││└───────────────────── ``` --- base/idset.jl | 1 + base/reflection.jl | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/base/idset.jl b/base/idset.jl index 6812c4ff3ceb0..0a4d4275b4231 100644 --- a/base/idset.jl +++ b/base/idset.jl @@ -12,6 +12,7 @@ IdSet{T}(itr) where {T} = union!(IdSet{T}(), itr) IdSet() = IdSet{Any}() copymutable(s::IdSet) = typeof(s)(s) +emptymutable(s::IdSet{T}, ::Type{U}=T) where {T,U} = IdSet{U}() copy(s::IdSet) = typeof(s)(s) isempty(s::IdSet) = isempty(s.dict) diff --git a/base/reflection.jl b/base/reflection.jl index 535ead8aedf92..5c3667dd5f60d 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -483,7 +483,7 @@ Determine whether type `T` was declared as a mutable type !!! compat "Julia 1.7" This function requires at least Julia 1.7. """ -function ismutabletype(@nospecialize(t::Type)) +function ismutabletype(@nospecialize t) t = unwrap_unionall(t) # TODO: what to do for `Union`? return isa(t, DataType) && t.name.flags & 0x2 == 0x2 @@ -496,7 +496,7 @@ end Determine whether type `T` was declared as a struct type (i.e. using the `struct` or `mutable struct` keyword). """ -function isstructtype(@nospecialize(t::Type)) +function isstructtype(@nospecialize t) @_pure_meta t = unwrap_unionall(t) # TODO: what to do for `Union`? @@ -511,7 +511,7 @@ end Determine whether type `T` was declared as a primitive type (i.e. using the `primitive` keyword). """ -function isprimitivetype(@nospecialize(t::Type)) +function isprimitivetype(@nospecialize t) @_pure_meta t = unwrap_unionall(t) # TODO: what to do for `Union`? @@ -543,7 +543,7 @@ julia> isbitstype(Complex) false ``` """ -isbitstype(@nospecialize(t::Type)) = (@_pure_meta; isa(t, DataType) && (t.flags & 0x8) == 0x8) +isbitstype(@nospecialize t) = (@_pure_meta; isa(t, DataType) && (t.flags & 0x8) == 0x8) """ isbits(x) @@ -1199,7 +1199,7 @@ end Similar to [`code_typed`](@ref), except the argument is a tuple type describing a full signature to query. """ -function code_typed_by_type(@nospecialize(tt::Type); +function code_typed_by_type(@nospecialize(tt#=::Type=#); optimize=true, debuginfo::Symbol=:default, world = get_world_counter(), @@ -1276,7 +1276,7 @@ function print_statement_costs(io::IO, @nospecialize(f), @nospecialize(t); kwarg print_statement_costs(io, tt; kwargs...) end -function print_statement_costs(io::IO, @nospecialize(tt::Type); +function print_statement_costs(io::IO, @nospecialize(tt#=::Type=#); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) matches = _methods_by_ftype(tt, -1, world) @@ -1306,7 +1306,7 @@ end print_statement_costs(args...; kwargs...) = print_statement_costs(stdout, args...; kwargs...) -function _which(@nospecialize(tt::Type), world=get_world_counter()) +function _which(@nospecialize(tt#=::Type=#), world=get_world_counter()) min_valid = RefValue{UInt}(typemin(UInt)) max_valid = RefValue{UInt}(typemax(UInt)) match = ccall(:jl_gf_invoke_lookup_worlds, Any, @@ -1341,7 +1341,7 @@ end Returns the method that would be called by the given type signature (as a tuple type). """ -function which(@nospecialize(tt::Type)) +function which(@nospecialize(tt#=::Type=#)) return _which(tt).method end