-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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: inter-procedural conditional constraint back-propagation #38905
Conversation
Slightly off-topic, but #37342 got accidentally linked for automatic closing. |
All tests turned green. Ready for review. |
Out of curiosity, any measurements regarding compile times? For example, building the sysimage, time to first plot etc. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
04655dc
to
b13fbcc
Compare
base/compiler/tfuncs.jl
Outdated
@@ -1648,7 +1648,7 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s | |||
if contains_is(argtypes_vec, Union{}) | |||
return Const(Union{}) | |||
end | |||
rt = abstract_call(interp, nothing, argtypes_vec, sv, -1).rt | |||
rt = widenconditional(abstract_call(interp, nothing, argtypes_vec, sv, -1).rt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now this change isn't necessary for this PR to work (abstract_call
no longer returns InterConditional
), but I think widenconditional
is better here since it may produce more accurate result since it can be widen to Const
, so let me include this as is.
Okay, I addressed the review comments, and this is good to go, I'd say. Any further comments or reviews ? |
ef2ab51
to
889add5
Compare
- `@isssa`/`@isslotnum` hacks circumvent JuliaLang/julia#37342 and gets rid of type assertions and accompanying inference overheads (they are really, really minor and negligible though) NOTE: well, we won't need them once JuliaLang/julia#38905 gets merged - improve inferrability around `pcexec` within `selective_eval!`
With #38905 merged, this shouldn't apply in general anymore. I am sure with `==` being a generic function, there will still be cases where using `===` will lead to more precise inference, but I don't think this is worth specifically mentioning in the performance tips.
With #38905 merged, this shouldn't apply in general anymore. I am sure with `==` being a generic function, there will still be cases where using `===` will lead to more precise inference, but I don't think this is worth specifically mentioning in the performance tips.
With JuliaLang#38905 merged, this shouldn't apply in general anymore. I am sure with `==` being a generic function, there will still be cases where using `===` will lead to more precise inference, but I don't think this is worth specifically mentioning in the performance tips.
With JuliaLang#38905 merged, this shouldn't apply in general anymore. I am sure with `==` being a generic function, there will still be cases where using `===` will lead to more precise inference, but I don't think this is worth specifically mentioning in the performance tips.
With JuliaLang#38905 merged, this shouldn't apply in general anymore. I am sure with `==` being a generic function, there will still be cases where using `===` will lead to more precise inference, but I don't think this is worth specifically mentioning in the performance tips.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation.
The PR #38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements #38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation. We also improve constant-prop' heuristics in these ways: - remove `const_prop_rettype_heuristic` since it handles rare cases, where const-prop' doens't seem to be worthwhile, e.g. it won't be so useful to try to propagate `Const(Tuple{DataType,DataType})` for `Const(convert)(::Const(Tuple{DataType,DataType}), ::Tuple{DataType,DataType} -> Tuple{DataType,DataType}` - rename `is_allconst` to `is_all_overridden` - also minor refactors and improvements added
The PR JuliaLang#38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements JuliaLang#38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation. We also improve constant-prop' heuristics in these ways: - remove `const_prop_rettype_heuristic` since it handles rare cases, where const-prop' doens't seem to be worthwhile, e.g. it won't be so useful to try to propagate `Const(Tuple{DataType,DataType})` for `Const(convert)(::Const(Tuple{DataType,DataType}), ::Tuple{DataType,DataType} -> Tuple{DataType,DataType}` - rename `is_allconst` to `is_all_overridden` - also minor refactors and improvements added
This is more generic and will allow packages to define custom `missing`-like types allowing to distinguish several kinds of missing values like in e.g. Stata and SAS. This should have no performance impact now thanks to #38905.
The PR JuliaLang#38905 only "back-propagates" conditional constraint (from callee to caller), but currently we don't "forward" it (caller to callee), and so inter-procedural constraint propagation won't happen for e.g.: ```julia ifelselike(cnd, x, y) = cnd ? x : y @test Base.return_types((Any,Int,)) do x, y ifelselike(isa(x, Int), x, y) end |> only == Int ``` This commit complements JuliaLang#38905 and enables further inter-procedural conditional constraint propagation by forwarding `Conditional` to callees when it imposes a constraint on any other argument, during constant propagation. We also improve constant-prop' heuristics in these ways: - remove `const_prop_rettype_heuristic` since it handles rare cases, where const-prop' doens't seem to be worthwhile, e.g. it won't be so useful to try to propagate `Const(Tuple{DataType,DataType})` for `Const(convert)(::Const(Tuple{DataType,DataType}), ::Tuple{DataType,DataType} -> Tuple{DataType,DataType}` - rename `is_allconst` to `is_all_overridden` - also minor refactors and improvements added
When I introduced this new lattice within #38905, I defined this in `Core` for some performance reason, but now I'm sure it is really necessary. Rather, it is easier to develop Julia-level inference routine in pure Julia, so this commit moves the definition of `InterConditional` to `Core.Compiler`.
When I introduced this new lattice within #38905, I defined this in `Core` for some performance reason, but now I'm sure it is really necessary. Rather, it is easier to develop Julia-level inference routine in pure Julia, so this commit moves the definition of `InterConditional` to `Core.Compiler`.
When I introduced this new lattice within #38905, I defined this in `Core` for some performance reason, but now I'm sure it is really necessary. Rather, it is easier to develop Julia-level inference routine in pure Julia, so this commit moves the definition of `InterConditional` to `Core.Compiler`.
When I introduced this new lattice within #38905, I defined this in `Core` for some performance reason, but now I'm sure it is really necessary. Rather, it is easier to develop Julia-level inference routine in pure Julia, so this commit moves the definition of `InterConditional` to `Core.Compiler`.
This is more generic and will allow packages to define custom `missing`-like types allowing to distinguish several kinds of missing values like in e.g. Stata and SAS (see https://github.com/nalimilan/TypedMissings.jl). This should have no performance impact now thanks to #38905.
This PR propagates
Conditional
s inter-procedurally when aConditional
at return site imposes a constraint on the call arguments.When inference exits local frame and the return type is annotated as
Conditional
, it will be converted intoInterConditional
object,which is implemented in
Core
and can be directly put into the globalcache. Finally after going back to caller frame,
InterConditional
willbe re-converted into
Conditional
in the context of the caller frame.improvements
So now some simple "is-wrapper" functions will propagate its constraint
as expected, e.g.:
(and now we don't need something like #38636)
benchmarks
A compile time comparison:
Here is a sample code that benefits from this PR:
caveats
Within the
Conditional
/InterConditional
framework, only a singleconstraint can be back-propagated inter-procedurally. This PR implements
a naive heuristic to "pick up" a constraint to be propagated when a
return type is a boolean. The heuristic may fail to select an
"interesting" constraint in some cases. For example, we may expect
::Expr
constraint to be imposed on the first argument ofMeta.isexpr
, but the current heuristic ends up picking up a constrainton the second argument (i.e.
ex.head === head
).I think We can get rid of this limitation by extending
Conditional
andInterConditional
so that they can convey multiple constraints, but I'd like to leave this
as a future work.
EDIT: 3df027a implements the "pick up" logic within a caller
(i.e. within
abstract_call_gf_by_type
), which allows us to choose aconstraint more appropriately, and now the
Meta.isexpr
case is fixed.Still there is a limitation of multiple conditional constraint
back-propagation; the following broken test case will explain the future
work.
findall(::Union{AbstractString,AbstractPattern}, ::AbstractString)
#38636