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

Inference loses precision with more information on types #52134

Open
ranocha opened this issue Nov 12, 2023 · 0 comments
Open

Inference loses precision with more information on types #52134

ranocha opened this issue Nov 12, 2023 · 0 comments
Labels
compiler:inference Type inference

Comments

@ranocha
Copy link
Member

ranocha commented Nov 12, 2023

I found this while looking into invalidations on Julia v1.10-rc1. The code

using SnoopCompileCore
invalidations = @snoopr begin using OrdinaryDiffEq end
using SnoopCompile
@show length(uinvalidated(invalidations))
trees = invalidation_trees(invalidations)

and investigating the last entry lead to

julia> trees[end]
inserting &(::Static.StaticInteger{X}, y::Union{Missing, Integer}) where X @ Static ~/.julia/packages/Static/dLrtk/src/Static.jl:489 invalidated:
   mt_backedges: 1: signature Tuple{typeof(&), Any, UInt8} triggered MethodInstance for ismutable(::Tuple{Vararg{T, _A}} where {T, _A}) (8 children)
                 2: signature Tuple{typeof(&), Any, UInt8} triggered MethodInstance for ismutable(::Function) (732 children)

julia> trees[end].mt_backedges[end].second |> ascend
Choose a call for analysis (q to quit):
 >   ismutable(::Function)
       pointer_from_objref(::Function)
         unsafe_convert(::Type{Ptr{T}}, ::Base.RefValue{T}) where T<:Function
           unsafe_convert(::Type{Ref{T}}, ::Base.RefValue{T}) where T<:Function
             utf8proc_decompose(::String, ::Int64, ::Ptr{Nothing}, ::Int64, ::Function)
               utf8proc_map(::String, ::Int64, ::Function)
                 utf8proc_map(::String, ::Int64)
                   normalize(::String, ::Symbol)
                     isfile_casesensitive(::String)
v                      active_project(::Bool)
ismutable(x) @ Base reflection.jl:517
┌ Warning: Some line information is missing, type-assignment may be incomplete
└ @ Cthulhu ~/.julia/packages/Cthulhu/Ndnbu/src/codeview.jl:144
517 ismutable(@nospecialize(x::Function))::Any = (@_total_meta; ((typeof(x::Function)::Type{<:Function}.name::Any.flags::Any & 0x2)::Any == 0x2)::Any)
Select a call to descend into or ↩ to ascend. [q]uit. [b]ookmark.
Toggles: [w]arn, [h]ide type-stable statements, [t]ype annotations, [s]yntax highlight for Source/LLVM/Native, [j]ump to source always.
Show: [S]ource code, [A]ST, [T]yped code, [L]LVM IR, [N]ative code
Actions: [E]dit source code, [R]evise and redisplay
 • typeof(x::Function)::Type{<:Function}.name
   runtime typeof(x::Function)::Type{<:Function}.name::Any.flags
   runtime typeof(x::Function)::Type{<:Function}.name::Any.flags::Any & 0x2
   runtime (typeof(x::Function)::Type{<:Function}.name::Any.flags::Any & 0x2)::Any == 0x2

An MWE reproducing the type inference problem is

julia> function foo(x)
           return typeof(first(x)).name.flags
       end
foo (generic function with 1 method)

julia> @code_warntype foo([1.0]) # fine
MethodInstance for foo(::Vector{Float64})
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Vector{Float64}
Body::UInt8
1%1 = Main.first(x)::Float64%2 = Main.typeof(%1)::Core.Const(Float64)
│   %3 = Base.getproperty(%2, :name)::Core.Const(typename(Float64))
│   %4 = Base.getproperty(%3, :flags)::Core.Const(0x04)
└──      return %4


julia> @code_warntype foo(Any[1.0]) # fine
MethodInstance for foo(::Vector{Any})
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Vector{Any}
Body::UInt8
1%1 = Main.first(x)::Any%2 = Main.typeof(%1)::DataType%3 = Base.getproperty(%2, :name)::Core.TypeName%4 = Base.getproperty(%3, :flags)::UInt8
└──      return %4


julia> @code_warntype foo(Real[1.0]) # bad
MethodInstance for foo(::Vector{Real})
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Vector{Real}
Body::Any
1%1 = Main.first(x)::Real%2 = Main.typeof(%1)::Type{<:Real}%3 = Base.getproperty(%2, :name)::Any%4 = Base.getproperty(%3, :flags)::Any
└──      return %4

julia> @code_warntype foo(Function[identity])
MethodInstance for foo(::Vector{Function})
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Vector{Function}
Body::Any
1$(Expr(:meta, :noinline))
│        nothing%3 = Base.getindex(x, 1)::Function%4 = Main.typeof(%3)::Type{<:Function}%5 = Base.getproperty(%4, :name)::Any%6 = Base.getproperty(%5, :flags)::Any
└──      return %6

Here, type inference gets worse with more information such as Type{<:Function} instead of DataType.

  1. Is there a way to fix this underlying issue?
  2. If there is no easy way to fix it, can I assume that typeof(x).name::Core.TypeName holds for every x to fix at least the invalidations soon in the code below?

ismutable(@nospecialize(x)) = (@_total_meta; typeof(x).name.flags & 0x2 == 0x2)

@ranocha ranocha added the compiler:inference Type inference label Nov 12, 2023
KristofferC pushed a commit that referenced this issue Nov 28, 2023
Related to #52134. It would be nice if the underlying inference issue
was fixed but this seems to be a hotfix for now.

I have seen this inference problem occurring in Julia v1.9, v1.10, and
current `master`. For example, on Julia v1.9.3, I get

```julia

julia> code_warntype(ismutable, (Function,))
MethodInstance for ismutable(::Function)
  from ismutable(x) @ Base reflection.jl:521
Arguments
  #self#::Core.Const(ismutable)
  x::Function
Body::Any
1 ─      nothing
│        nothing
│   %3 = Base.typeof(x)::Type{<:Function}
│   %4 = Base.getproperty(%3, :name)::Any
│   %5 = Base.getproperty(%4, :flags)::Any
│   %6 = (%5 & 0x02)::Any
│   %7 = (%6 == 0x02)::Any
└──      return %7
```

This causes some invalidations when `using OrdinaryDiffEq`.
mkitti pushed a commit to mkitti/julia that referenced this issue Dec 9, 2023
Related to JuliaLang#52134. It would be nice if the underlying inference issue
was fixed but this seems to be a hotfix for now.

I have seen this inference problem occurring in Julia v1.9, v1.10, and
current `master`. For example, on Julia v1.9.3, I get

```julia

julia> code_warntype(ismutable, (Function,))
MethodInstance for ismutable(::Function)
  from ismutable(x) @ Base reflection.jl:521
Arguments
  #self#::Core.Const(ismutable)
  x::Function
Body::Any
1 ─      nothing
│        nothing
│   %3 = Base.typeof(x)::Type{<:Function}
│   %4 = Base.getproperty(%3, :name)::Any
│   %5 = Base.getproperty(%4, :flags)::Any
│   %6 = (%5 & 0x02)::Any
│   %7 = (%6 == 0x02)::Any
└──      return %7
```

This causes some invalidations when `using OrdinaryDiffEq`.
KristofferC pushed a commit that referenced this issue Dec 12, 2023
Related to #52134. It would be nice if the underlying inference issue
was fixed but this seems to be a hotfix for now.

I have seen this inference problem occurring in Julia v1.9, v1.10, and
current `master`. For example, on Julia v1.9.3, I get

```julia

julia> code_warntype(ismutable, (Function,))
MethodInstance for ismutable(::Function)
  from ismutable(x) @ Base reflection.jl:521
Arguments
  #self#::Core.Const(ismutable)
  x::Function
Body::Any
1 ─      nothing
│        nothing
│   %3 = Base.typeof(x)::Type{<:Function}
│   %4 = Base.getproperty(%3, :name)::Any
│   %5 = Base.getproperty(%4, :flags)::Any
│   %6 = (%5 & 0x02)::Any
│   %7 = (%6 == 0x02)::Any
└──      return %7
```

This causes some invalidations when `using OrdinaryDiffEq`.

(cherry picked from commit df40bab)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:inference Type inference
Projects
None yet
Development

No branches or pull requests

1 participant