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

optimizer: inline abstract union-split callsite #44512

Merged
merged 1 commit into from
Mar 30, 2022
Merged

Commits on Mar 30, 2022

  1. optimizer: inline abstract union-split callsite

    Currently the optimizer handles abstract callsite only when there is a
    single dispatch candidate (in most cases), and so inlining and static-dispatch
    are prohibited when the callsite is union-split (in other word, union-split
    happens only when all the dispatch candidates are concrete).
    
    However, there are certain patterns of code (most notably our Julia-level compiler code)
    that inherently need to deal with abstract callsite.
    The following example is taken from `Core.Compiler` utility:
    ```julia
    julia> @inline isType(@nospecialize t) = isa(t, DataType) && t.name === Type.body.name
    isType (generic function with 1 method)
    
    julia> code_typed((Any,)) do x # abstract, but no union-split, successful inlining
               isType(x)
           end |> only
    CodeInfo(
    1 ─ %1 = (x isa Main.DataType)::Bool
    └──      goto #3 if not %1
    2 ─ %3 = π (x, DataType)
    │   %4 = Base.getfield(%3, :name)::Core.TypeName
    │   %5 = Base.getfield(Type{T}, :name)::Core.TypeName
    │   %6 = (%4 === %5)::Bool
    └──      goto #4
    3 ─      goto #4
    4 ┄ %9 = φ (#2 => %6, #3 => false)::Bool
    └──      return %9
    ) => Bool
    
    julia> code_typed((Union{Type,Nothing},)) do x # abstract, union-split, unsuccessful inlining
               isType(x)
           end |> only
    CodeInfo(
    1 ─ %1 = (isa)(x, Nothing)::Bool
    └──      goto #3 if not %1
    2 ─      goto #4
    3 ─ %4 = Main.isType(x)::Bool
    └──      goto #4
    4 ┄ %6 = φ (#2 => false, #3 => %4)::Bool
    └──      return %6
    ) => Bool
    ```
    (note that this is a limitation of the inlining algorithm, and so any
    user-provided hints like callsite inlining annotation doesn't help here)
    
    This commit enables inlining and static dispatch for abstract union-split callsite.
    The core idea here is that we can simulate our dispatch semantics by
    generating `isa` checks in order of the specialities of dispatch candidates:
    ```julia
    julia> code_typed((Union{Type,Nothing},)) do x # union-split, unsuccessful inlining
                      isType(x)
                  end |> only
    CodeInfo(
    1 ─ %1  = (isa)(x, Nothing)::Bool
    └──       goto #3 if not %1
    2 ─       goto #9
    3 ─ %4  = (isa)(x, Type)::Bool
    └──       goto #8 if not %4
    4 ─ %6  = π (x, Type)
    │   %7  = (%6 isa Main.DataType)::Bool
    └──       goto #6 if not %7
    5 ─ %9  = π (%6, DataType)
    │   %10 = Base.getfield(%9, :name)::Core.TypeName
    │   %11 = Base.getfield(Type{T}, :name)::Core.TypeName
    │   %12 = (%10 === %11)::Bool
    └──       goto #7
    6 ─       goto #7
    7 ┄ %15 = φ (#5 => %12, #6 => false)::Bool
    └──       goto #9
    8 ─       Core.throw(ErrorException("fatal error in type inference (type bound)"))::Union{}
    └──       unreachable
    9 ┄ %19 = φ (#2 => false, #7 => %15)::Bool
    └──       return %19
    ) => Bool
    ```
    
    Inlining/static-dispatch of abstract union-split callsite will improve
    the performance in such situations (and so this commit will improve the
    latency of our JIT compilation). Especially, this commit helps us avoid
    excessive specializations of `Core.Compiler` code by statically-resolving
    `@nospecialize`d callsites, and as the result, the # of precompiled
    statements is now reduced from  `1956` ([`master`](dc45d77)) to `1901` (this commit).
    
    And also, as a side effect, the implementation of our inlining algorithm
    gets much simplified now since we no longer need the previous special
    handlings for abstract callsites.
    
    One possible drawback would be increased code size.
    This change seems to certainly increase the size of sysimage,
    but I think these numbers are in an acceptable range:
    > [`master`](dc45d77)
    ```
    ❯ du -sh usr/lib/julia/*
     17M    usr/lib/julia/corecompiler.ji
    188M    usr/lib/julia/sys-o.a
    164M    usr/lib/julia/sys.dylib
     23M    usr/lib/julia/sys.dylib.dSYM
    101M    usr/lib/julia/sys.ji
    ```
    
    > this commit
    ```
    ❯ du -sh usr/lib/julia/*
     17M    usr/lib/julia/corecompiler.ji
    190M    usr/lib/julia/sys-o.a
    166M    usr/lib/julia/sys.dylib
     23M    usr/lib/julia/sys.dylib.dSYM
    102M    usr/lib/julia/sys.ji
    ```
    aviatesk committed Mar 30, 2022
    Configuration menu
    Copy the full SHA
    408c140 View commit details
    Browse the repository at this point in the history