From 0cbb6850fb50b9a58778a4c0cb3a6b6004b2d8b3 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Sat, 24 Jul 2021 15:45:38 +0200 Subject: [PATCH] improve constant prop in gcd (#41258) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- base/intfuncs.jl | 20 ++++++++++++++------ test/intfuncs.jl | 8 ++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index fd793f94dd783..169bbe313a620 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -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) @@ -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...) diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 27dccdbca9a28..40cda879193d4 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -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