Skip to content

Propagate array lengths#4

Closed
pchintalapudi wants to merge 64 commits intopc/allocation-hoistingfrom
pc/multi-d-array-propagation
Closed

Propagate array lengths#4
pchintalapudi wants to merge 64 commits intopc/allocation-hoistingfrom
pc/multi-d-array-propagation

Conversation

@pchintalapudi
Copy link
Copy Markdown
Owner

Constructing the Regex touched in this commit can represent a
significant fraction (e.g. half or better) of the runtime of
the DateFormat method touched in this commit. To make this
DateFormat method more efficient, let's lift that Regex
construction out of that method body.
@pchintalapudi pchintalapudi force-pushed the pc/allocation-hoisting branch from 36abcf3 to 29ae1a1 Compare January 5, 2022 06:31
@pchintalapudi pchintalapudi force-pushed the pc/multi-d-array-propagation branch from 04e5202 to a351c5e Compare January 5, 2022 07:02
aviatesk and others added 26 commits January 5, 2022 23:11
…prop' callsite (JuliaLang#43347)

Makes full use of constant-propagation, by addressing this [TODO](https://github.com/JuliaLang/julia/blob/00734c5fd045316a00d287ca2c0ec1a2eef6e4d1/base/compiler/ssair/inlining.jl#L1212).
Here is a performance improvement from JuliaLang#43287:
```julia
ulia> using BenchmarkTools

julia> X = rand(ComplexF32, 64, 64);

julia> dst = reinterpret(reshape, Float32, X);

julia> src = copy(dst);

julia> @Btime copyto!($dst, $src);
  50.819 μs (1 allocation: 32 bytes) # v1.6.4
  41.081 μs (0 allocations: 0 bytes) # this commit
```

fixes JuliaLang#43287
…g#43515)

On Darwin, when generating anonymous handles, we hardcoded `/tmp` as the
beginning path, but that's bad behavior if the user has explicitly
requested (via setting `$TEMP` or `$TEMPDIR`, etc...) that we not do so.
Let's use `uv_os_tmpdir()` to put things in the correct place as much as
we can, defaulting to `/tmp` if anything goes wrong inside of libuv.
Without this, an attempt to call this function ends with:

ERROR: LoadError: UndefVarError: pipe not defined
Stacktrace:
 [1] TCPSocket(fd::RawFD)
   @ Sockets /nix/store/w0xq12x4gb3ng0pdh1r9ir4z14pmr3pp-julia-bin-1.7.0/share/julia/stdlib/v1.7/Sockets/src/Sockets.jl:100
 [2] top-level scope

Also add a test for this function. It designed only for Linux, because
I don't know whether it would work on other platforms.
This allows us to make breaking changes on the `main` branch of cryptic
without breaking anything here
JuliaLang#41085 was not released in Julia 1.7, and only appears in Julia 1.8.
Lift expensive Regex construction from DateFormat method body.
Fixes JuliaLang#29576
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
Co-authored-by: Lilith Orion Hafner <60898866+LilithHafner@users.noreply.github.com>
…43683)

Without this, `JL_LLVM_VERSION` is undefined and defaults to 0, so the
branch for older LLVM is always taken.
Prevents loading of `libjulia-codegen` into `opt`.

Co-authored-by: Jameson Nash <vtjnash@gmail.com>
Co-authored-by: Prem Chintalapudi <premc@csail.mit.edu>
There is no plausible reason for this. And indeed, this patch fixes
compilation for the libjulia_jll builder on Yggdrasil which currently fails
due to the absence of libssp.

The removed line was introduced in PR JuliaLang#41936 but without any justification
that I could discern, so it might have just slipped in there accidentally.
Regression introduced by ca11c2f (when tests fail or are interrupted)
aviatesk and others added 21 commits January 8, 2022 16:26
…JuliaLang#43587)

Obvious errors are usually caught in higher places like `convert`, but
it's better to have error checks at each builtin level in order to enable
early bail out from errorneous code compilation (when somehow it does not
rely on common abstraction). These checks are also useful for third
consumers like JET.
…uliaLang#43684)

Previously, we had the machinery to load per-UUID, but some tooling
needs to be able to load all preferences (without loading
`Preferences.jl`), so it's better to support being able to load
everything from just within Base.
…FO` (for e.g. when Git is not available) (JuliaLang#43717)

* `base/version_git.sh`: fix the default value for `Base.GIT_VERSION_INFO` (for e.g. when Git is not available)

* Use single-quotes to avoid the need to backslash-escape the double quotes
@pchintalapudi pchintalapudi force-pushed the pc/multi-d-array-propagation branch from 1f3cb02 to 5c5f559 Compare January 9, 2022 23:21
pchintalapudi pushed a commit that referenced this pull request Jan 24, 2022
…43226)

In order to allow `Argument`s to be printed nicely.

> before
```julia
julia> code_typed((Float64,)) do x
           sin(x)
       end
1-element Vector{Any}:
 CodeInfo(
1 ─ %1 = invoke Main.sin(_2::Float64)::Float64
└──      return %1
) => Float64

julia> code_typed((Bool,Any,Any)) do c, x, y
           z = c ? x : y
           z
       end
1-element Vector{Any}:
 CodeInfo(
1 ─      goto #3 if not c
2 ─      goto #4
3 ─      nothing::Nothing
4 ┄ %4 = φ (#2 => _3, #3 => _4)::Any
└──      return %4
) => Any
```

> after
```julia
julia> code_typed((Float64,)) do x
           sin(x)
       end
1-element Vector{Any}:
 CodeInfo(
1 ─ %1 = invoke Main.sin(x::Float64)::Float64
└──      return %1
) => Float64

julia> code_typed((Bool,Any,Any)) do c, x, y
           z = c ? x : y
           z
       end
1-element Vector{Any}:
 CodeInfo(
1 ─      goto #3 if not c
2 ─      goto #4
3 ─      nothing::Nothing
4 ┄ %4 = φ (#2 => x, #3 => y)::Any
└──      return %4
) => Any
```
pchintalapudi pushed a commit that referenced this pull request Mar 25, 2022
This commit implements a simple optimization within `sroa_mutables!` to
eliminate `isdefined` call by checking load-forwardability of the field.
This optimization may be especially useful to eliminate extra allocation
of `Core.Box` involved with a capturing closure, e.g.:
```julia
julia> callit(f, args...) = f(args...);

julia> function isdefined_elim()
           local arr::Vector{Any}
           callit() do
               arr = Any[]
           end
           return arr
       end;

julia> code_typed(isdefined_elim)
```
```diff
diff --git a/_master.jl b/_pr.jl
index 3aa40ba20e5..11eccf65f32 100644
--- a/_master.jl
+++ b/_pr.jl
@@ -1,15 +1,8 @@
 1-element Vector{Any}:
  CodeInfo(
-1 ─ %1  = Core.Box::Type{Core.Box}
-│   %2  = %new(%1)::Core.Box
-│   %3  = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Vector{Any}, svec(Any, Int64), 0, :(:ccall), Vector{Any}, 0, 0))::Vector{Any}
-│         Core.setfield!(%2, :contents, %3)::Vector{Any}
-│   %5  = Core.isdefined(%2, :contents)::Bool
-└──       goto #3 if not %5
-2 ─       goto #4
-3 ─       $(Expr(:throw_undef_if_not, :arr, false))::Any
-4 ┄ %9  = Core.getfield(%2, :contents)::Any
-│         Core.typeassert(%9, Vector{Any})::Vector{Any}
-│   %11 = π (%9, Vector{Any})
-└──       return %11
+1 ─ %1 = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Vector{Any}, svec(Any, Int64), 0, :(:ccall), Vector{Any}, 0, 0))::Vector{Any}
+└──      goto #3 if not true
+2 ─      goto #4
+3 ─      $(Expr(:throw_undef_if_not, :arr, false))::Any
+4 ┄      return %1
 ) => Vector{Any}
```
DilumAluthge pushed a commit that referenced this pull request Mar 28, 2022
Follows up JuliaLang#44708 -- in that PR I missed the most obvious optimization
opportunity, i.e. we can safely eliminate `isdefined` checks when all
fields are defined at allocation site.
This change allows us to eliminate capturing closure constructions when
the body and callsite of capture closure is available within a optimized
frame, e.g.:
```julia
function abmult(r::Int, x0)
    if r < 0
        r = -r
    end
    f = x -> x * r
    return @inline f(x0)
end
```
```diff
diff --git a/_master.jl b/_pr.jl
index ea06d865b75..c38f221090f 100644
--- a/_master.jl
+++ b/_pr.jl
@@ -1,24 +1,19 @@
 julia> @code_typed abmult(-3, 3)
 CodeInfo(
-1 ── %1  = Core.Box::Type{Core.Box}
-│    %2  = %new(%1, r@_2)::Core.Box
-│    %3  = Core.isdefined(%2, :contents)::Bool
-└───       goto #3 if not %3
+1 ──       goto #3 if not true
 2 ──       goto #4
 3 ──       $(Expr(:throw_undef_if_not, :r, false))::Any
-4 ┄─ %7  = (r@_2 < 0)::Any
-└───       goto JuliaLang#9 if not %7
-5 ── %9  = Core.isdefined(%2, :contents)::Bool
-└───       goto #7 if not %9
+4 ┄─ %4  = (r@_2 < 0)::Any
+└───       goto JuliaLang#9 if not %4
+5 ──       goto #7 if not true
 6 ──       goto #8
 7 ──       $(Expr(:throw_undef_if_not, :r, false))::Any
-8 ┄─ %13 = -r@_2::Any
-9 ┄─ %14 = φ (#4 => r@_2, #8 => %13)::Any
-│    %15 = Core.isdefined(%2, :contents)::Bool
-└───       goto JuliaLang#11 if not %15
+8 ┄─ %9  = -r@_2::Any
+9 ┄─ %10 = φ (#4 => r@_2, #8 => %9)::Any
+└───       goto JuliaLang#11 if not true
 10 ─       goto JuliaLang#12
 11 ─       $(Expr(:throw_undef_if_not, :r, false))::Any
-12 ┄ %19 = (x0 * %14)::Any
+12 ┄ %14 = (x0 * %10)::Any
 └───       goto JuliaLang#13
-13 ─       return %19
+13 ─       return %14
 ) => Any
```
pchintalapudi pushed a commit that referenced this pull request Mar 30, 2022
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 JuliaLang#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 JuliaLang#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  `2005` ([`master`](f782430)) to `1912` (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`](f782430)
```
❯ du -shk usr/lib/julia/*
17604	usr/lib/julia/corecompiler.ji
194072	usr/lib/julia/sys-o.a
169424	usr/lib/julia/sys.dylib
23784	usr/lib/julia/sys.dylib.dSYM
103772	usr/lib/julia/sys.ji
```

> this commit
```
❯ du -shk usr/lib/julia/*
17512	usr/lib/julia/corecompiler.ji
195588	usr/lib/julia/sys-o.a
170908	usr/lib/julia/sys.dylib
23776	usr/lib/julia/sys.dylib.dSYM
105360	usr/lib/julia/sys.ji
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.