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

RFC: address issue #20882 #20889

Merged
merged 8 commits into from
Mar 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ Language changes
`Vector{T} = Array{T,1}` or a `const` assignment.

* Experimental feature: `x^n` for integer literals `n` (e.g. `x^3`
or `x^-3`) is now lowered to `x^Val{n}`, to enable compile-time
specialization for literal integer exponents ([#20530]).
or `x^-3`) is now lowered to `Base.literal_pow(^, x, Val{n})`, to enable
compile-time specialization for literal integer exponents ([#20530], [#20889]).

Breaking changes
----------------
Expand Down
25 changes: 12 additions & 13 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -195,29 +195,28 @@ end
^(x::Number, p::Integer) = power_by_squaring(x,p)
^(x, p::Integer) = power_by_squaring(x,p)

# x^p for any literal integer p is lowered to x^Val{p},
# x^p for any literal integer p is lowered to Base.literal_pow(^, x, Val{p})
# to enable compile-time optimizations specialized to p.
# However, we still need a fallback that calls the general ^.
# To avoid ambiguities for methods that dispatch on the
# first argument, we dispatch the fallback via internal_pow.
# However, we still need a fallback that calls the function ^ which may either
# mean Base.^ or something else, depending on context.
# We mark these @inline since if the target is marked @inline,
# we want to make sure that gets propagated,
# even if it is over the inlining threshold.
@inline ^(x, p) = internal_pow(x, p)
@inline internal_pow{p}(x, ::Type{Val{p}}) = x^p
@inline literal_pow{p}(f, x, ::Type{Val{p}}) = f(x,p)

# Restrict inlining to hardware-supported arithmetic types, which
# are fast enough to benefit from inlining. This also makes it
# easier to override ^ without having to override the Val method.
# are fast enough to benefit from inlining.
const HWReal = Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float32,Float64}
const HWNumber = Union{HWReal, Complex{<:HWReal}, Rational{<:HWReal}}

# inference.jl has complicated logic to inline x^2 and x^3 for
# numeric types. In terms of Val we can do it much more simply:
@inline internal_pow(x::HWNumber, ::Type{Val{0}}) = one(x)
@inline internal_pow(x::HWNumber, ::Type{Val{1}}) = x
@inline internal_pow(x::HWNumber, ::Type{Val{2}}) = x*x
@inline internal_pow(x::HWNumber, ::Type{Val{3}}) = x*x*x
# numeric types. In terms of Val we can do it much more simply.
# (The first argument prevents unexpected behavior if a function ^
# is defined that is not equal to Base.^)
@inline literal_pow(::typeof(^), x::HWNumber, ::Type{Val{0}}) = one(x)
@inline literal_pow(::typeof(^), x::HWNumber, ::Type{Val{1}}) = x
@inline literal_pow(::typeof(^), x::HWNumber, ::Type{Val{2}}) = x*x
@inline literal_pow(::typeof(^), x::HWNumber, ::Type{Val{3}}) = x*x*x

Comment on lines +219 to 220
Copy link
Member

Choose a reason for hiding this comment

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

Is there a particular reason to stop at 3 rather than e.g. 4? Of course there's virtually no limit, but 4 seems like a common power (much more than 5 and above I'd say). This could help with things like JuliaStats/HypothesisTests.jl#238.

Copy link
Member

Choose a reason for hiding this comment

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

There's an increasing loss of accuracy at higher powers. This definition for 3 is already inaccurate by a noticeable bit in some cases, so 4+ will potentially be progressively worse from there (though (x*x)^2 seems pretty well-compensated due to the symmetric shape, as these things go).

Copy link
Member

Choose a reason for hiding this comment

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

OK, thanks. So are you suggesting we should add a definition using (x*x)^2?

# b^p mod m

Expand Down
8 changes: 5 additions & 3 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,11 @@ end
Exponentiation operator. If `x` is a matrix, computes matrix exponentiation.

If `y` is an `Int` literal (e.g. `2` in `x^2` or `-3` in `x^-3`), the Julia code
`x^y` is transformed by the compiler to `x^Val{y}`, to enable compile-time
specialization on the value of the exponent. (As a default fallback,
however, `x^Val{y}` simply calls the `^(x,y)` function.)
`x^y` is transformed by the compiler to `Base.literal_pow(^, x, Val{y})`, to
enable compile-time specialization on the value of the exponent.
(As a default fallback we have `Base.literal_pow(^, x, Val{y}) = ^(x,y)`,
where usually `^ == Base.^` unless `^` has been defined in the calling
namespace.)

```jldoctest
julia> 3^5
Expand Down
2 changes: 1 addition & 1 deletion src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -2057,7 +2057,7 @@

((and (eq? f '^) (length= e 4) (integer? (cadddr e)))
(expand-forms
`(call ^ ,(caddr e) (call (core apply_type) (top Val) ,(cadddr e)))))
`(call (top literal_pow) ^ ,(caddr e) (call (core apply_type) (top Val) ,(cadddr e)))))

((and (eq? f '*) (length= e 4))
(expand-transposed-op
Expand Down
13 changes: 11 additions & 2 deletions test/numbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2903,9 +2903,12 @@ end
end

import Base.^
immutable PR20530; end
struct PR20530; end
struct PR20889; x; end
^(::PR20530, p::Int) = 1
^{p}(::PR20530, ::Type{Val{p}}) = 2
^(t::PR20889, b) = t.x + b
^(t::PR20889, b::Integer) = t.x + b
Base.literal_pow{p}(::typeof(^), ::PR20530, ::Type{Val{p}}) = 2
@testset "literal powers" begin
x = PR20530()
p = 2
Expand All @@ -2923,6 +2926,12 @@ immutable PR20530; end
end
end
end
@test PR20889(2)^3 == 5
end
module M20889 # do we get the expected behavior without importing Base.^?
struct PR20889; x; end
^(t::PR20889, b) = t.x + b
Base.Test.@test PR20889(2)^3 == 5
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no need for the Base.Test. here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have to put these tests in a different module than the other tests because I'm explicitly testing for what happens when I do not import Base.^. Since they are in a different module, I shouldn't get Base.Test.@test automatically, since I haven't done using Base.Test.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, right. Don't mind me then.

end

@testset "iszero" begin
Expand Down