Skip to content

Commit 55eb09d

Browse files
committed
Merge pull request #45613 from JuliaLang/avi/concretemath
improve concrete-foldability of core math functions
1 parent 1110f2b commit 55eb09d

File tree

5 files changed

+61
-30
lines changed

5 files changed

+61
-30
lines changed

base/intfuncs.jl

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -248,20 +248,20 @@ end
248248

249249
# ^ for any x supporting *
250250
to_power_type(x) = convert(Base._return_type(*, Tuple{typeof(x), typeof(x)}), x)
251-
@noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p,
252-
string("Cannot raise an integer x to a negative power ", p, '.',
253-
"\nConvert input to float.")))
254-
@noinline throw_domerr_powbysq(::Integer, p) = throw(DomainError(p,
255-
string("Cannot raise an integer x to a negative power ", p, '.',
256-
"\nMake x or $p a float by adding a zero decimal ",
257-
"(e.g., 2.0^$p or 2^$(float(p)) instead of 2^$p), ",
258-
"or write 1/x^$(-p), float(x)^$p, x^float($p) or (x//1)^$p")))
259-
@noinline throw_domerr_powbysq(::AbstractMatrix, p) = throw(DomainError(p,
260-
string("Cannot raise an integer matrix x to a negative power ", p, '.',
261-
"\nMake x a float matrix by adding a zero decimal ",
262-
"(e.g., [2.0 1.0;1.0 0.0]^$p instead ",
263-
"of [2 1;1 0]^$p), or write float(x)^$p or Rational.(x)^$p")))
264-
function power_by_squaring(x_, p::Integer)
251+
@noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p, LazyString(
252+
"Cannot raise an integer x to a negative power ", p, ".",
253+
"\nConvert input to float.")))
254+
@noinline throw_domerr_powbysq(::Integer, p) = throw(DomainError(p, LazyString(
255+
"Cannot raise an integer x to a negative power ", p, ".",
256+
"\nMake x or ", p, " a float by adding a zero decimal ",
257+
"(e.g., 2.0^", p, " or 2^", float(p), " instead of 2^", p, ")",
258+
"or write 1/x^", -p, ", float(x)^", p, ", x^float(", p, ") or (x//1)^", p, ".")))
259+
@noinline throw_domerr_powbysq(::AbstractMatrix, p) = throw(DomainError(p, LazyString(
260+
"Cannot raise an integer matrix x to a negative power ", p, ".",
261+
"\nMake x a float matrix by adding a zero decimal ",
262+
"(e.g., [2.0 1.0;1.0 0.0]^", p, " instead of [2 1;1 0]^", p, ")",
263+
"or write float(x)^", p, " or Rational.(x)^", p, ".")))
264+
@assume_effects :terminates_locally function power_by_squaring(x_, p::Integer)
265265
x = to_power_type(x_)
266266
if p == 1
267267
return copy(x)

base/special/exp.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,11 @@ const J_TABLE = (0x0000000000000000, 0xaac00b1afa5abcbe, 0x9b60163da9fb3335, 0xa
175175
0xa66f0f9c1cb64129, 0x93af252b376bba97, 0xacdf3ac948dd7273, 0x99df50765b6e4540, 0x9faf6632798844f8,
176176
0xa12f7bfdad9cbe13, 0xaeef91d802243c88, 0x874fa7c1819e90d8, 0xacdfbdba3692d513, 0x62efd3c22b8f71f1, 0x74afe9d96b2a23d9)
177177

178-
@inline function table_unpack(ind)
178+
# XXX we want to mark :consistent-cy here so that this function can be concrete-folded,
179+
# because the effect analysis currently can't prove it in the presence of `@inbounds` or
180+
# `:boundscheck`, but still the access to `J_TABLE` is really safe here
181+
Base.@assume_effects :consistent @inline function table_unpack(ind::Int32)
182+
ind = ind & 255 + 1 # 255 == length(J_TABLE) - 1
179183
j = @inbounds J_TABLE[ind]
180184
jU = reinterpret(Float64, JU_CONST | (j&JU_MASK))
181185
jL = reinterpret(Float64, JL_CONST | (j>>8))
@@ -211,7 +215,7 @@ end
211215
r = muladd(N_float, LogBo256U(base, T), x)
212216
r = muladd(N_float, LogBo256L(base, T), r)
213217
k = N >> 8
214-
jU, jL = table_unpack(N&255 + 1)
218+
jU, jL = table_unpack(N)
215219
small_part = muladd(jU, expm1b_kernel(base, r), jL) + jU
216220

217221
if !(abs(x) <= SUBNORM_EXP(base, T))
@@ -236,7 +240,7 @@ end
236240
r = muladd(N_float, LogBo256U(base, T), x)
237241
r = muladd(N_float, LogBo256L(base, T), r)
238242
k = N >> 8
239-
jU, jL = table_unpack(N&255 + 1)
243+
jU, jL = table_unpack(N)
240244
very_small = muladd(jU, expm1b_kernel(base, r), jL)
241245
small_part = muladd(jU,xlo,very_small) + jU
242246
if !(abs(x) <= SUBNORM_EXP(base, T))
@@ -438,7 +442,7 @@ function expm1(x::Float64)
438442
r = muladd(N_float, LogBo256U(Val(:ℯ), T), x)
439443
r = muladd(N_float, LogBo256L(Val(:ℯ), T), r)
440444
k = Int64(N >> 8)
441-
jU, jL = table_unpack(N&255 +1)
445+
jU, jL = table_unpack(N)
442446
p = expm1b_kernel(Val(:ℯ), r)
443447
twopk = reinterpret(Float64, (1023+k) << 52)
444448
twopnk = reinterpret(Float64, (1023-k) << 52)

base/special/log.jl

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ const t_log_Float64 = ((0.0,0.0),(0.007782140442941454,-8.865052917267247e-13),
9292
(0.6853040030982811,6.383161517064652e-13),(0.6892332812385575,2.5144230728376075e-13),
9393
(0.6931471805601177,-1.7239444525614835e-13))
9494

95-
9695
# Float32 lookup table
9796
# to generate values:
9897
# N=16
@@ -156,7 +155,12 @@ logbU(::Type{Float64},::Val{10}) = 0.4342944819032518
156155
logbL(::Type{Float64},::Val{10}) = 1.098319650216765e-17
157156

158157
# Procedure 1
159-
@inline function log_proc1(y::Float64,mf::Float64,F::Float64,f::Float64,jp::Int,base=Val(:ℯ))
158+
# XXX we want to mark :consistent-cy here so that this function can be concrete-folded,
159+
# because the effect analysis currently can't prove it in the presence of `@inbounds` or
160+
# `:boundscheck`, but still the access to `t_log_Float64` is really safe here
161+
Base.@assume_effects :consistent @inline function log_proc1(y::Float64,mf::Float64,F::Float64,f::Float64,base=Val(:ℯ))
162+
jp = unsafe_trunc(Int,128.0*F)-127
163+
160164
## Steps 1 and 2
161165
@inbounds hi,lo = t_log_Float64[jp]
162166
l_hi = mf* 0.6931471805601177 + hi
@@ -211,8 +215,13 @@ end
211215
return fma(m_hi, u, fma(m_lo, u, m_hi*fma(fma(-u,f,2(f-u)), g, q)))
212216
end
213217

218+
# Procedure 1
219+
# XXX we want to mark :consistent-cy here so that this function can be concrete-folded,
220+
# because the effect analysis currently can't prove it in the presence of `@inbounds` or
221+
# `:boundscheck`, but still the access to `t_log_Float32` is really safe here
222+
Base.@assume_effects :consistent @inline function log_proc1(y::Float32,mf::Float32,F::Float32,f::Float32,base=Val(:ℯ))
223+
jp = unsafe_trunc(Int,128.0f0*F)-127
214224

215-
@inline function log_proc1(y::Float32,mf::Float32,F::Float32,f::Float32,jp::Int,base=Val(:ℯ))
216225
## Steps 1 and 2
217226
@inbounds hi = t_log_Float32[jp]
218227
l = mf*0.6931471805599453 + hi
@@ -232,6 +241,7 @@ end
232241
Float32(logb(Float32, base)*(l + (u + q)))
233242
end
234243

244+
# Procedure 2
235245
@inline function log_proc2(f::Float32,base=Val(:ℯ))
236246
## Step 1
237247
# compute in higher precision
@@ -281,9 +291,8 @@ function _log(x::Float64, base, func)
281291
mf = Float64(m)
282292
F = (y + 3.5184372088832e13) - 3.5184372088832e13 # 0x1p-7*round(0x1p7*y)
283293
f = y-F
284-
jp = unsafe_trunc(Int,128.0*F)-127
285294

286-
return log_proc1(y,mf,F,f,jp,base)
295+
return log_proc1(y,mf,F,f,base)
287296
elseif x == 0.0
288297
-Inf
289298
elseif isnan(x)
@@ -317,9 +326,8 @@ function _log(x::Float32, base, func)
317326
mf = Float32(m)
318327
F = (y + 65536.0f0) - 65536.0f0 # 0x1p-7*round(0x1p7*y)
319328
f = y-F
320-
jp = unsafe_trunc(Int,128.0f0*F)-127
321329

322-
log_proc1(y,mf,F,f,jp,base)
330+
log_proc1(y,mf,F,f,base)
323331
elseif x == 0f0
324332
-Inf32
325333
elseif isnan(x)
@@ -352,9 +360,8 @@ function log1p(x::Float64)
352360
mf = Float64(m)
353361
F = (y + 3.5184372088832e13) - 3.5184372088832e13 # 0x1p-7*round(0x1p7*y)
354362
f = (y - F) + c*s #2^m(F+f) = 1+x = z+c
355-
jp = unsafe_trunc(Int,128.0*F)-127
356363

357-
log_proc1(y,mf,F,f,jp)
364+
log_proc1(y,mf,F,f)
358365
elseif x == -1.0
359366
-Inf
360367
elseif isnan(x)
@@ -385,9 +392,8 @@ function log1p(x::Float32)
385392
mf = Float32(m)
386393
F = (y + 65536.0f0) - 65536.0f0 # 0x1p-7*round(0x1p7*y)
387394
f = (y - F) + s*c #2^m(F+f) = 1+x = z+c
388-
jp = unsafe_trunc(Int,128.0*F)-127
389395

390-
log_proc1(y,mf,F,f,jp)
396+
log_proc1(y,mf,F,f)
391397
elseif x == -1f0
392398
-Inf32
393399
elseif isnan(x)

base/special/rem_pio2.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ function fromfraction(f::Int128)
125125
return (z1,z2)
126126
end
127127

128-
function paynehanek(x::Float64)
128+
# XXX we want to mark :consistent-cy here so that this function can be concrete-folded,
129+
# because the effect analysis currently can't prove it in the presence of `@inbounds` or
130+
# `:boundscheck`, but still the accesses to `INV_2PI` are really safe here
131+
Base.@assume_effects :consistent function paynehanek(x::Float64)
129132
# 1. Convert to form
130133
#
131134
# x = X * 2^k,

test/math.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,3 +1425,21 @@ end
14251425
for T in (Float32, Float64)
14261426
@test Core.Compiler.is_foldable(Base.infer_effects(^, (T,Int)))
14271427
end
1428+
1429+
# test constant-foldability
1430+
for fn in (:sin, :cos, :tan, :log, :log2, :log10, :log1p, :exponent, :sqrt, :cbrt,
1431+
# TODO :asin, :atan, :acos, :sinh, :cosh, :tanh, :asinh, :acosh, :atanh,
1432+
# TODO :exp, :exp2, :exp10, :expm1
1433+
)
1434+
for T in (Float32, Float64)
1435+
f = getfield(@__MODULE__, fn)
1436+
eff = Base.infer_effects(f, (T,))
1437+
if Core.Compiler.is_foldable(eff)
1438+
@test true
1439+
else
1440+
@error "bad effects found for $f(::$T)" eff
1441+
@test false
1442+
end
1443+
end
1444+
end
1445+
@test Core.Compiler.is_foldable(Base.infer_effects(^, (Float32,Int)))

0 commit comments

Comments
 (0)