JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.72k stars 5.48k forks source link

better compile-time evaluation of type expressions #7060

Closed stevengj closed 10 years ago

stevengj commented 10 years ago

Right now, Julia does not seem to do a good job of partially evaluating type expressions that are known at compile time. For example:

f{T}(x::T) = T <: Real ? 1 : 2

should be compiled to f(x) = 1 for f(3) or f(x) = 2 for f(4+5im), but code_native(f, (Int,)) reveals that this is not the case: in fact, the code for this function is quite involved. Fixed by #7088.

It would be nice to have this work for cases where using dispatch is impractical, especially in macros. For example, as discussed in #7033, I would like to generalize the @horner macro to use a better algorithm for cases where the argument is complex but the coefficients are real, but it should inline to the correct code depending upon the argument type.

Another example that should be possible to evaluate at compile time is:

g{T}(x::T) = method_exists(one, (T,)) ? 1 : 2

Explicit method_exists checks are used, for example, in reducedim.jl and multimedia.jl. (_It might be good to evaluate this statically only if method_exists returns true, to preserve the current dynamic behavior._)

A third example mentioned on the mailing list in the context of better type promotion for Array*Number for SIUnits quantities is that it would be nice to have isleaftype(T) evaluated at compile time. (Partially fixed by #7088.)

stevengj commented 10 years ago

On the other hand, it looks like isa does, in fact, inline to a constant expression that gets optimized out. For example, with

f(x) = isa(x, Real) ? 1 : 2

if you do code_native(f, (Int,)) it is clear that the conditional has been optimized out.

stevengj commented 10 years ago

Thanks to @carnaval for his improvements! Some things that are still not compiled statically:

g{T}(x::Array{T}) = isleaftype(T) ? 1 : 2
code_native(g, (Array{Int},))

or

g(x::Array) = isleaftype(eltype(x)) ? 1 : 2
code_native(g, (Array{Int,1},))
stevengj commented 10 years ago

Another one that just bit me in #7125: type-inference is performed before dead-code elimination. Hence, for example:

julia> f(x) = isa(x, Integer) ? 1 : 1.0
f (generic function with 1 method)

julia> [f(i) for i in 1:2]
2-element Array{Union(Float64,Int64),1}:
 1
 1

instead of the return type being Array{Int}.

JeffBezanson commented 10 years ago

Type inference and DCE can be iterated any number of times to improve the resulting code --- there is no one correct ordering. But doing constant prop as part of type inference would fix this case (constant prop being type inference with singleton types).

stevengj commented 10 years ago

Another oddity:

f(x) = isa(x, Complex) ? 1 : 2

does not get statically evaluated, as code_native(f, (Int,)) shows, even though static evaluation worked for the isa(x, Real) case above.

Update: Fixed by 98189bc6444898a341264bc6c02e9564b016e6db

JeffBezanson commented 10 years ago

This issue is covered by #6822 and #5560.