Skip to content

Commit

Permalink
improve constant prop in gcd (#41258)
Browse files Browse the repository at this point in the history
With this PR:

```julia
julia> f(x) = x * (2//3)
f (generic function with 1 method)

julia> @code_typed f(2.3)
CodeInfo(
1 ─ %1 = Base.mul_float(x, 0.6666666666666666)::Float64
└──      return %1
) => Float64
```

It is a bit unfortunate to have to resort to `@pure` here, but I could
not get it to constant fold any other way. I don't think this usage
should be problematic since the method only accepts `BitInteger`s and
only ever calls methods that really shouldn't be redefined.

fixes #32024
  • Loading branch information
simeonschaub authored Jul 24, 2021
1 parent 073af4a commit 0cbb685
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 6 deletions.
20 changes: 14 additions & 6 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,22 @@ function gcd(a::T, b::T) where T<:Integer
checked_abs(a)
end

# binary GCD (aka Stein's) algorithm
# about 1.7x (2.1x) faster for random Int64s (Int128s)
function gcd(a::T, b::T) where T<:BitInteger
a == 0 && return checked_abs(b)
b == 0 && return checked_abs(a)
r = _gcd(a, b)
signbit(r) && __throw_gcd_overflow(a, b)
return r
end
@noinline __throw_gcd_overflow(a, b) = throw(OverflowError("gcd($a, $b) overflows"))

# binary GCD (aka Stein's) algorithm
# about 1.7x (2.1x) faster for random Int64s (Int128s)
# Unfortunately, we need to manually annotate this as `@pure` to work around #41694. Since
# this is used in the Rational constructor, constant prop is something we do care about here.
# This does call generic functions, so it might not be completely sound, but since `_gcd` is
# restricted to BitIntegers, it is probably fine in practice.
@pure function _gcd(a::T, b::T) where T<:BitInteger
za = trailing_zeros(a)
zb = trailing_zeros(b)
k = min(za, zb)
Expand All @@ -65,11 +76,8 @@ function gcd(a::T, b::T) where T<:BitInteger
v >>= trailing_zeros(v)
end
r = u << k
# T(r) would throw InexactError; we want OverflowError instead
r > typemax(T) && __throw_gcd_overflow(a, b)
r % T
return r % T
end
@noinline __throw_gcd_overflow(a, b) = throw(OverflowError("gcd($a, $b) overflows"))

"""
lcm(x, y...)
Expand Down
8 changes: 8 additions & 0 deletions test/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -479,3 +479,11 @@ end
for b in [-100:-2; 2:100;]
@test Base.ndigits0z(0, b) == 0
end

@testset "constant prop in gcd" begin
ci = code_typed(() -> gcd(14, 21))[][1]
@test ci.code == Any[Core.ReturnNode(7)]

ci = code_typed(() -> 14 // 21)[][1]
@test ci.code == Any[Core.ReturnNode(2 // 3)]
end

0 comments on commit 0cbb685

Please sign in to comment.