Skip to content

Conversation

@aviatesk
Copy link
Member

We discussed that for certain methods like :totally-declared method or
@nospecialized method we may want to only do constant propagation
while disabling preceding only-type-level inference.

This commit implements a simple infrastructure for such early constant
propagation, and especially setups early concrete evaluation pass.

This commit should recover the regression reported at #44776 (comment)
while preserving the advantages and correctness improvements of the
@assume_effects-based concrete evaluation enabled in the PR.


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

@aviatesk aviatesk added the compiler:inference Type inference label Apr 11, 2022
@nanosoldier
Copy link
Collaborator

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

Base automatically changed from avi/pure2effects to master April 11, 2022 14:34
@aviatesk aviatesk force-pushed the avi/early_const_prop branch from f8d4736 to 2f390c6 Compare April 12, 2022 03:31
@aviatesk aviatesk added the latency Latency label Apr 12, 2022
@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.

Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this still manage to compute the backedges and world ages? Actually, it seems like any use of the @effects macro to mark a function suffers the same problems as @pure with not handling method-redefinition-edges?

@aviatesk
Copy link
Member Author

I think the backedge insertion at this line is enough for handling method-redefinition-edge:

add_backedge!(result.edge, sv)

And it seems to be functional:

julia> Base.@assume_effects :total typeintersect_effects(@nospecialize(a), @nospecialize(b)) =
           ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)
typeintersect_effects (generic function with 1 method)

julia> Base.@pure typeintersect_pure(@nospecialize(a), @nospecialize(b)) =
           ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)
typeintersect_pure (generic function with 1 method)

julia> main_effects() = typeintersect_effects(Int, String)
main_effects (generic function with 1 method)

julia> main_pure() = typeintersect_pure(Int, String)
main_pure (generic function with 1 method)

julia> function main()
           return main_effects(), main_pure()
       end
main (generic function with 1 method)

julia> main()
(Union{}, Union{})

julia> function typeintersect_effects(@nospecialize(a), @nospecialize(b))
           println("effects: ", a, '&', b)
           return ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)
       end
typeintersect_effects (generic function with 1 method)

julia> function typeintersect_pure(@nospecialize(a), @nospecialize(b))
           println("pure: ", a, '&', b)
           return ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)
       end
typeintersect_pure (generic function with 1 method)

julia> main()
effects: Int64&String
(Union{}, Union{})

This PR also adds a backedge to concrete-evaled method instance in a case when we do early concrete evaluation:

add_backedge!(val.const_result.mi, sv)

May I miss some other points?

@vtjnash
Copy link
Member

vtjnash commented Apr 18, 2022

The @pure code intentionally does not add that one edge, since it will miss all of the transitive edges anyways. You would need to mark main_effects here to see a similar effect. Otherwise, you could have put the assume_effects on the ccall where it was not subject to redefinition or edges anyways.

We discussed that for certain methods like `:total`ly-declared method or
`@nospecialize`d method we may want to only do constant propagation
while disabling preceding only-type-level inference.

This commit implements a simple infrastructure for such early constant
propagation, and especially setups early concrete evaluation pass.

This commit should recover the regression reported at <#44776 (comment)>
while preserving the advantages and correctness improvements of the
`@assume_effects`-based concrete evaluation enabled in the PR.
@aviatesk
Copy link
Member Author

It seems like JIT-compilation within Core._call_in_world_total adds transitive edges?

julia> typeintersect_effects(@nospecialize(a), @nospecialize(b)) =
           ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)
typeintersect_effects (generic function with 1 method)

julia> typeintersect_pure(@nospecialize(a), @nospecialize(b)) =
           ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)
typeintersect_pure (generic function with 1 method)

julia> Base.@assume_effects :total main_effects() = typeintersect_effects(Int, String)
main_effects (generic function with 1 method)

julia> Base.@pure main_pure() = typeintersect_pure(Int, String)
main_pure (generic function with 1 method)

julia> mktuple() = (println("super effectful"); (main_effects(), main_pure()))
mktuple (generic function with 1 method)

julia> main() = mktuple()
main (generic function with 1 method)

julia> @code_typed optimize=false main()
CodeInfo(
1%1 = Main.mktuple()::Core.Const((Union{}, Union{}))
└──      return %1
) => Tuple{Core.TypeofBottom, Core.TypeofBottom}

julia> main()
super effectful
(Union{}, Union{})

julia> function typeintersect_effects(@nospecialize(a), @nospecialize(b))
           ret = ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)
           if ret === Union{}
               return nothing
           else
               return ret
           end
       end
typeintersect_effects (generic function with 1 method)

julia> function typeintersect_pure(@nospecialize(a), @nospecialize(b))
           ret = ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)
           if ret === Union{}
               return nothing
           else
               return ret
           end
       end
typeintersect_pure (generic function with 1 method)

julia> @code_typed optimize=false main()
CodeInfo(
1%1 = Main.mktuple()::Core.Const((nothing, nothing))
└──      return %1
) => Tuple{Nothing, Nothing}

julia> main()
super effectful
(nothing, nothing)

@aviatesk aviatesk force-pushed the avi/early_const_prop branch from 2f390c6 to d3bf5db Compare April 19, 2022 03:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

compiler:inference Type inference latency Latency

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants