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: backward constraint propagation from call signatures #40880

Closed

Conversation

aviatesk
Copy link
Member

@aviatesk aviatesk commented May 19, 2021

This PR implements another (limited) backward analysis pass in abstract
interpretation; it exploits signatures of matching methods and refines
types of slots.

Here are couple of examples where these changes will improve the accuracy:

generic function example

addint(a::Int, b::Int) = a + b
@test Base.infer_return_type((Any,Any,)) do a, b
    c = addint(a, b)
    return a, b, c # now the compiler understands `a::Int`, `b::Int`
end == Tuple{Int,Int,Int}

typeassert example

@test Base.infer_return_type((Any,)) do a
    a::Int
    return a # now the compiler understands `a::Int`
end == Int

Unlike Conditional constrained type propagation, this type refinement
information isn't encoded within any lattice element, but rather they
are propagated within the newly added field frame.curr_stmt_change of
frame::InferenceState.
For now this commit exploits refinement information available from call
signatures of generic functions and typeassert.


@aviatesk aviatesk added the compiler:inference Type inference label May 19, 2021
@KristofferC
Copy link
Member

Fixes #37866?

@aviatesk
Copy link
Member Author

Oh, I didn't know there is already an issue on this.

And yeah, this PR will fix that:

julia> function find_first_above_5(v::Vector{Union{Nothing, Float64}})
           for x in v
               if x > 5.0
                   return x # x > 5.0 is MethodError for Nothing so can assume ::Float64
               end
           end
           return 0.0
       end
find_first_above_5 (generic function with 1 method)

julia> x = Union{Nothing, Float64}[1.0, 2.0, 3.0, 6.0, 4.0]
5-element Vector{Union{Nothing, Float64}}:
 1.0
 2.0
 3.0
 6.0
 4.0

julia> find_first_above_5(x)
6.0

julia> Core.Compiler.return_types(find_first_above_5, Tuple{Vector{Union{Nothing, Float64}}})
1-element Vector{Any}:
 Float64

julia> code_warntype(find_first_above_5, Tuple{Vector{Union{Nothing, Float64}}})
MethodInstance for find_first_above_5(::Vector{Union{Nothing, Float64}})
  from find_first_above_5(v::Vector{Union{Nothing, Float64}}) in Main at REPL[1]:1
Arguments
  #self#::Core.Const(find_first_above_5)
  v::Vector{Union{Nothing, Float64}}
Locals
  @_3::Union{Nothing, Tuple{Union{Nothing, Float64}, Int64}}
  x::Union{Nothing, Float64}
Body::Float64
1%1  = v::Vector{Union{Nothing, Float64}}
│         (@_3 = Base.iterate(%1))
│   %3  = (@_3 === nothing)::Bool%4  = Base.not_int(%3)::Bool
└──       goto #6 if not %4
2%6  = @_3::Tuple{Union{Nothing, Float64}, Int64}
│         (x = Core.getfield(%6, 1))
│   %8  = Core.getfield(%6, 2)::Int64%9  = (x > 5.0)::Bool
└──       goto #4 if not %9
3return x::Float64
4 ─       (@_3 = Base.iterate(%1, %8))
│   %13 = (@_3 === nothing)::Bool%14 = Base.not_int(%13)::Bool
└──       goto #6 if not %14
5 ─       goto #2
6return 0.0

@aviatesk
Copy link
Member Author

@nanosoldier runbenchmarks(ALL, vs=":master")

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @christopher-dG

@aviatesk
Copy link
Member Author

@nanosoldier runbenchmarks(ALL, vs=":master")

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @christopher-dG

@aviatesk
Copy link
Member Author

@vtjnash can I ask your review on this ?

@aviatesk aviatesk force-pushed the multiupdates branch 4 times, most recently from 780e927 to ae85281 Compare June 3, 2021 03:54
@StefanKarpinski
Copy link
Member

Bump!

@aviatesk
Copy link
Member Author

@nanosoldier runbenchmarks(ALL, vs=":master")

@nanosoldier
Copy link
Collaborator

Something went wrong when running your job:

NanosoldierError: error when preparing/pushing to report repo: failed process: Process(setenv(`git push`; dir="/run/media/system/data/nanosoldier/workdir/NanosoldierReports"), ProcessExited(1)) [1]

Unfortunately, the logs could not be uploaded.
cc @christopher-dG

@KristofferC
Copy link
Member

I think @vtjnash has to manually upload the result.

@aviatesk
Copy link
Member Author

hm, does that mean a part of the system is kinda broken now ?

@KristofferC
Copy link
Member

Yes, the files now get too big to upload to github. JuliaCI/Nanosoldier.jl#97

@vtjnash
Copy link
Member

vtjnash commented Sep 23, 2021

Yeah, I'm not sure why the files suddenly changed from 20MB to 100MB

https://github.com/JuliaCI/NanosoldierReports/blob/master/benchmark/by_hash/06b2bf2_vs_5e8d7ed/report.md

@aviatesk
Copy link
Member Author

Okay, thanks for uploading it anyway.

@nanosoldier
Copy link
Collaborator

Something went wrong when running your job:

NanosoldierError: error when preparing/pushing to report repo: failed process: Process(setenv(`git push`; dir="/run/media/system/data/nanosoldier/workdir/NanosoldierReports"), ProcessExited(1)) [1]

Unfortunately, the logs could not be uploaded.
cc @christopher-dG

@vchuravy
Copy link
Member

A rebase would be fantastic!

Should also fix #38274

@JeffBezanson
Copy link
Member

Bump again!

test/compiler/inference.jl Outdated Show resolved Hide resolved
aviatesk added a commit that referenced this pull request Jul 23, 2024
Separated from #40880.
This subtle adjustment allows for more accurate type inference in the
following kind of cases:
```julia
function condition_object_update2(x)
    cond = x isa Int
    if cond # `cond` is known to be `Const(true)` within this branch
        return !cond ? nothing : x # ::Int
    else
        return  cond ? nothing : 1 # ::Int
    end
end
@test Base.infer_return_type(condition_object_update2, (Any,)) == Int
```
This PR implements another (limited) backward analysis pass in abstract
interpretation; it exploits signatures of matching methods and refines
types of slots.

Here are couple of examples where these changes will improve the accuracy:

> generic function example
```julia
addint(a::Int, b::Int) = a + b
@test Base.infer_return_type((Any,Any,)) do a, b
    c = addint(a, b)
    return a, b, c # now the compiler understands `a::Int`, `b::Int`
end == Tuple{Int,Int,Int}
```

> `typeassert` example
```julia
@test Base.infer_return_type((Any,)) do a
    a::Int
    return a # now the compiler understands `a::Int`
end == Int
```

Unlike `Conditional` constrained type propagation, this type refinement
information isn't encoded within any lattice element, but rather they
are propagated within the newly added field `frame.curr_stmt_change` of
`frame::InferenceState`.
For now this commit exploits refinement information available from call
signatures of generic functions and `typeassert`.
@aviatesk aviatesk changed the base branch from master to avi/refine-condition-object July 23, 2024 10:00
@aviatesk
Copy link
Member Author

@nanosoldier runbenchmarks("inference", vs=":master")

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here.

aviatesk added a commit that referenced this pull request Jul 24, 2024
Separated from #40880.
This subtle adjustment allows for more accurate type inference in the
following kind of cases:
```julia
function condition_object_update2(x)
    cond = x isa Int
    if cond # `cond` is known to be `Const(true)` within this branch
        return !cond ? nothing : x # ::Int
    else
        return  cond ? nothing : 1 # ::Int
    end
end
@test Base.infer_return_type(condition_object_update2, (Any,)) == Int
```

Also cleans up typelattice.jl a bit.
@aviatesk aviatesk deleted the branch JuliaLang:avi/refine-condition-object July 24, 2024 06:36
@aviatesk aviatesk closed this Jul 24, 2024
@nsajko
Copy link
Contributor

nsajko commented Jul 24, 2024

How come this is closed? Will there be a followup? I was hoping the linked issues will finally be fixed.

@aviatesk
Copy link
Member Author

I'm not sure. It might be because the base branch of this PR was the one I just merged a little while ago. I don't know how to reopen it, so I'll prepare a new PR.

@aviatesk
Copy link
Member Author

Now this PR is replaced by #55229.

lazarusA pushed a commit to lazarusA/julia that referenced this pull request Jul 24, 2024
Separated from JuliaLang#40880.
This subtle adjustment allows for more accurate type inference in the
following kind of cases:
```julia
function condition_object_update2(x)
    cond = x isa Int
    if cond # `cond` is known to be `Const(true)` within this branch
        return !cond ? nothing : x # ::Int
    else
        return  cond ? nothing : 1 # ::Int
    end
end
@test Base.infer_return_type(condition_object_update2, (Any,)) == Int
```

Also cleans up typelattice.jl a bit.
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

Successfully merging this pull request may close these issues.

8 participants