Skip to content

Commit

Permalink
Lower x^n (literal n) to Base.literal_pow(^, x, Val{n}) (#20889)
Browse files Browse the repository at this point in the history
Fixes #20882
  • Loading branch information
ajkeller34 authored and StefanKarpinski committed Mar 10, 2017
1 parent e91065c commit 75c2e72
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 21 deletions.
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

# 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
end

@testset "iszero" begin
Expand Down

0 comments on commit 75c2e72

Please sign in to comment.