Skip to content

Commit 0cbb685

Browse files
authored
improve constant prop in gcd (#41258)
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
1 parent 073af4a commit 0cbb685

File tree

2 files changed

+22
-6
lines changed

2 files changed

+22
-6
lines changed

base/intfuncs.jl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,22 @@ function gcd(a::T, b::T) where T<:Integer
4747
checked_abs(a)
4848
end
4949

50-
# binary GCD (aka Stein's) algorithm
51-
# about 1.7x (2.1x) faster for random Int64s (Int128s)
5250
function gcd(a::T, b::T) where T<:BitInteger
5351
a == 0 && return checked_abs(b)
5452
b == 0 && return checked_abs(a)
53+
r = _gcd(a, b)
54+
signbit(r) && __throw_gcd_overflow(a, b)
55+
return r
56+
end
57+
@noinline __throw_gcd_overflow(a, b) = throw(OverflowError("gcd($a, $b) overflows"))
58+
59+
# binary GCD (aka Stein's) algorithm
60+
# about 1.7x (2.1x) faster for random Int64s (Int128s)
61+
# Unfortunately, we need to manually annotate this as `@pure` to work around #41694. Since
62+
# this is used in the Rational constructor, constant prop is something we do care about here.
63+
# This does call generic functions, so it might not be completely sound, but since `_gcd` is
64+
# restricted to BitIntegers, it is probably fine in practice.
65+
@pure function _gcd(a::T, b::T) where T<:BitInteger
5566
za = trailing_zeros(a)
5667
zb = trailing_zeros(b)
5768
k = min(za, zb)
@@ -65,11 +76,8 @@ function gcd(a::T, b::T) where T<:BitInteger
6576
v >>= trailing_zeros(v)
6677
end
6778
r = u << k
68-
# T(r) would throw InexactError; we want OverflowError instead
69-
r > typemax(T) && __throw_gcd_overflow(a, b)
70-
r % T
79+
return r % T
7180
end
72-
@noinline __throw_gcd_overflow(a, b) = throw(OverflowError("gcd($a, $b) overflows"))
7381

7482
"""
7583
lcm(x, y...)

test/intfuncs.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,3 +479,11 @@ end
479479
for b in [-100:-2; 2:100;]
480480
@test Base.ndigits0z(0, b) == 0
481481
end
482+
483+
@testset "constant prop in gcd" begin
484+
ci = code_typed(() -> gcd(14, 21))[][1]
485+
@test ci.code == Any[Core.ReturnNode(7)]
486+
487+
ci = code_typed(() -> 14 // 21)[][1]
488+
@test ci.code == Any[Core.ReturnNode(2 // 3)]
489+
end

0 commit comments

Comments
 (0)