Skip to content

Commit e96270a

Browse files
committed
Make FFTW relocatable for PackageCompiler
Because we need to know the FFTW vendor pretty early (at precompile time to generate code, and at runtime) we need to load the path during build (which bakes it into the precompile image), however we need to update that path on next load within `__init__()` in case we're a part of PackageCompiler and we need to be able to move things around without requiring a recompile. So we change all accesses of `libfftw3{,f}` to be through a `Ref{String}()`, then update that ref within `__init__()`. In the future, accesses of `FFTW_jll.libfftw3` will be through a function, rather than a direct variable access, and we'll be able to get these things for free, but that's still a ways down the line.
1 parent df8f937 commit e96270a

File tree

4 files changed

+64
-48
lines changed

4 files changed

+64
-48
lines changed

src/FFTW.jl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,17 @@ function __init__()
2323
Base.depwarn("JULIA_FFTW_PROVIDER is deprecated; use FFTW.set_provider!() instead", :JULIA_FFTW_PROVIDER)
2424
end
2525

26-
# Hook FFTW threads up to our partr runtime
26+
# Hook FFTW threads up to our partr runtime, and re-assign the
27+
# libfftw3{,f} refs at runtime, since we may have relocated and
28+
# changed the path to the library since the last time we precompiled.
2729
@static if fftw_provider == "fftw"
2830
fftw_init_threads()
31+
libfftw3[] = FFTW_jll.libfftw3_path
32+
libfftw3f[] = FFTW_jll.libfftw3f_path
33+
end
34+
@static if fftw_provider == "mkl"
35+
libfftw3[] = MKL_jll.libmkl_rt_path
36+
libfftw3f[] = MKL_jll.libmkl_rt_path
2937
end
3038
end
3139

src/fft.jl

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function plan_r2r end
5656
## FFT: Implement fft by calling fftw.
5757

5858
const version = VersionNumber(split(unsafe_string(cglobal(
59-
(:fftw_version,libfftw3), UInt8)), ['-', ' '])[2])
59+
(:fftw_version,libfftw3[]), UInt8)), ['-', ' '])[2])
6060

6161
## Direction of FFT
6262

@@ -141,39 +141,39 @@ alignment_of(A::FakeArray) = Int32(0)
141141
@exclusive function export_wisdom(fname::AbstractString)
142142
f = ccall(:fopen, Ptr{Cvoid}, (Cstring,Cstring), fname, :w)
143143
systemerror("could not open wisdom file $fname for writing", f == C_NULL)
144-
ccall((:fftw_export_wisdom_to_file,libfftw3), Cvoid, (Ptr{Cvoid},), f)
144+
ccall((:fftw_export_wisdom_to_file,libfftw3[]), Cvoid, (Ptr{Cvoid},), f)
145145
ccall(:fputs, Int32, (Ptr{UInt8},Ptr{Cvoid}), " "^256, f) # no NUL, hence no Cstring
146-
ccall((:fftwf_export_wisdom_to_file,libfftw3f), Cvoid, (Ptr{Cvoid},), f)
146+
ccall((:fftwf_export_wisdom_to_file,libfftw3f[]), Cvoid, (Ptr{Cvoid},), f)
147147
ccall(:fclose, Cvoid, (Ptr{Cvoid},), f)
148148
end
149149

150150
@exclusive function import_wisdom(fname::AbstractString)
151151
f = ccall(:fopen, Ptr{Cvoid}, (Cstring,Cstring), fname, :r)
152152
systemerror("could not open wisdom file $fname for reading", f == C_NULL)
153-
if ccall((:fftw_import_wisdom_from_file,libfftw3),Int32,(Ptr{Cvoid},),f)==0||
154-
ccall((:fftwf_import_wisdom_from_file,libfftw3f),Int32,(Ptr{Cvoid},),f)==0
153+
if ccall((:fftw_import_wisdom_from_file,libfftw3[]),Int32,(Ptr{Cvoid},),f)==0||
154+
ccall((:fftwf_import_wisdom_from_file,libfftw3f[]),Int32,(Ptr{Cvoid},),f)==0
155155
error("failed to import wisdom from $fname")
156156
end
157157
ccall(:fclose, Cvoid, (Ptr{Cvoid},), f)
158158
end
159159

160160
@exclusive function import_system_wisdom()
161-
if ccall((:fftw_import_system_wisdom,libfftw3), Int32, ()) == 0 ||
162-
ccall((:fftwf_import_system_wisdom,libfftw3f), Int32, ()) == 0
161+
if ccall((:fftw_import_system_wisdom,libfftw3[]), Int32, ()) == 0 ||
162+
ccall((:fftwf_import_system_wisdom,libfftw3f[]), Int32, ()) == 0
163163
error("failed to import system wisdom")
164164
end
165165
end
166166

167167
@exclusive function forget_wisdom()
168-
ccall((:fftw_forget_wisdom,libfftw3), Cvoid, ())
169-
ccall((:fftwf_forget_wisdom,libfftw3f), Cvoid, ())
168+
ccall((:fftw_forget_wisdom,libfftw3[]), Cvoid, ())
169+
ccall((:fftwf_forget_wisdom,libfftw3f[]), Cvoid, ())
170170
end
171171

172172
# Threads
173173

174174
@exclusive function set_num_threads(nthreads::Integer)
175-
ccall((:fftw_plan_with_nthreads,libfftw3), Cvoid, (Int32,), nthreads)
176-
ccall((:fftwf_plan_with_nthreads,libfftw3f), Cvoid, (Int32,), nthreads)
175+
ccall((:fftw_plan_with_nthreads,libfftw3[]), Cvoid, (Int32,), nthreads)
176+
ccall((:fftwf_plan_with_nthreads,libfftw3f[]), Cvoid, (Int32,), nthreads)
177177
end
178178

179179
# pointer type for fftw_plan (opaque pointer)
@@ -187,9 +187,9 @@ const NO_TIMELIMIT = -1.0 # from fftw3.h
187187

188188
# only call these when fftwlock is held:
189189
unsafe_set_timelimit(precision::fftwTypeDouble,seconds) =
190-
ccall((:fftw_set_timelimit,libfftw3), Cvoid, (Float64,), seconds)
190+
ccall((:fftw_set_timelimit,libfftw3[]), Cvoid, (Float64,), seconds)
191191
unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
192-
ccall((:fftwf_set_timelimit,libfftw3f), Cvoid, (Float64,), seconds)
192+
ccall((:fftwf_set_timelimit,libfftw3f[]), Cvoid, (Float64,), seconds)
193193
@exclusive set_timelimit(precision, seconds) = unsafe_set_timelimit(precision, seconds)
194194

195195
# Array alignment mod 16:
@@ -210,9 +210,9 @@ unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
210210
convert(Int32, convert(Int64, pointer(A)) % 16)
211211
else
212212
alignment_of(A::StridedArray{T}) where {T<:fftwDouble} =
213-
ccall((:fftw_alignment_of, libfftw3), Int32, (Ptr{T},), A)
213+
ccall((:fftw_alignment_of, libfftw3[]), Int32, (Ptr{T},), A)
214214
alignment_of(A::StridedArray{T}) where {T<:fftwSingle} =
215-
ccall((:fftwf_alignment_of, libfftw3f), Int32, (Ptr{T},), A)
215+
ccall((:fftwf_alignment_of, libfftw3f[]), Int32, (Ptr{T},), A)
216216
end
217217

218218
# FFTWPlan (low-level)
@@ -268,9 +268,9 @@ unsafe_convert(::Type{PlanPtr}, p::FFTWPlan) = p.plan
268268

269269
# these functions should only be called while the fftwlock is held
270270
unsafe_destroy_plan(plan::FFTWPlan{<:fftwDouble}) =
271-
ccall((:fftw_destroy_plan,libfftw3), Cvoid, (PlanPtr,), plan)
271+
ccall((:fftw_destroy_plan,libfftw3[]), Cvoid, (PlanPtr,), plan)
272272
unsafe_destroy_plan(plan::FFTWPlan{<:fftwSingle}) =
273-
ccall((:fftwf_destroy_plan,libfftw3f), Cvoid, (PlanPtr,), plan)
273+
ccall((:fftwf_destroy_plan,libfftw3f[]), Cvoid, (PlanPtr,), plan)
274274

275275
const deferred_destroy_lock = ReentrantLock() # lock protecting the deferred_destroy_plans list
276276
const deferred_destroy_plans = FFTWPlan[]
@@ -331,19 +331,19 @@ end
331331
#################################################################################################
332332

333333
cost(plan::FFTWPlan{<:fftwDouble}) =
334-
ccall((:fftw_cost,libfftw3), Float64, (PlanPtr,), plan)
334+
ccall((:fftw_cost,libfftw3[]), Float64, (PlanPtr,), plan)
335335
cost(plan::FFTWPlan{<:fftwSingle}) =
336-
ccall((:fftwf_cost,libfftw3f), Float64, (PlanPtr,), plan)
336+
ccall((:fftwf_cost,libfftw3f[]), Float64, (PlanPtr,), plan)
337337

338338
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwDouble})
339339
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
340-
ccall((:fftw_flops,libfftw3), Cvoid,
340+
ccall((:fftw_flops,libfftw3[]), Cvoid,
341341
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
342342
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
343343
end
344344
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwSingle})
345345
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
346-
ccall((:fftwf_flops,libfftw3f), Cvoid,
346+
ccall((:fftwf_flops,libfftw3f[]), Cvoid,
347347
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
348348
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
349349
end
@@ -374,9 +374,9 @@ const has_sprint_plan = version >= v"3.3.4" && fftw_provider == "fftw"
374374

375375
@static if has_sprint_plan
376376
sprint_plan_(plan::FFTWPlan{<:fftwDouble}) =
377-
ccall((:fftw_sprint_plan,libfftw3), Ptr{UInt8}, (PlanPtr,), plan)
377+
ccall((:fftw_sprint_plan,libfftw3[]), Ptr{UInt8}, (PlanPtr,), plan)
378378
sprint_plan_(plan::FFTWPlan{<:fftwSingle}) =
379-
ccall((:fftwf_sprint_plan,libfftw3f), Ptr{UInt8}, (PlanPtr,), plan)
379+
ccall((:fftwf_sprint_plan,libfftw3f[]), Ptr{UInt8}, (PlanPtr,), plan)
380380
function sprint_plan(plan::FFTWPlan)
381381
p = sprint_plan_(plan)
382382
str = unsafe_string(p)
@@ -457,49 +457,49 @@ _colmajorstrides(p) = ()
457457
# Execute
458458

459459
unsafe_execute!(plan::FFTWPlan{<:fftwDouble}) =
460-
ccall((:fftw_execute,libfftw3), Cvoid, (PlanPtr,), plan)
460+
ccall((:fftw_execute,libfftw3[]), Cvoid, (PlanPtr,), plan)
461461

462462
unsafe_execute!(plan::FFTWPlan{<:fftwSingle}) =
463-
ccall((:fftwf_execute,libfftw3f), Cvoid, (PlanPtr,), plan)
463+
ccall((:fftwf_execute,libfftw3f[]), Cvoid, (PlanPtr,), plan)
464464

465465
unsafe_execute!(plan::cFFTWPlan{T},
466466
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} =
467-
ccall((:fftw_execute_dft,libfftw3), Cvoid,
467+
ccall((:fftw_execute_dft,libfftw3[]), Cvoid,
468468
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
469469

470470
unsafe_execute!(plan::cFFTWPlan{T},
471471
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
472-
ccall((:fftwf_execute_dft,libfftw3f), Cvoid,
472+
ccall((:fftwf_execute_dft,libfftw3f[]), Cvoid,
473473
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
474474

475475
unsafe_execute!(plan::rFFTWPlan{Float64,FORWARD},
476476
X::StridedArray{Float64}, Y::StridedArray{Complex{Float64}}) =
477-
ccall((:fftw_execute_dft_r2c,libfftw3), Cvoid,
477+
ccall((:fftw_execute_dft_r2c,libfftw3[]), Cvoid,
478478
(PlanPtr,Ptr{Float64},Ptr{Complex{Float64}}), plan, X, Y)
479479

480480
unsafe_execute!(plan::rFFTWPlan{Float32,FORWARD},
481481
X::StridedArray{Float32}, Y::StridedArray{Complex{Float32}}) =
482-
ccall((:fftwf_execute_dft_r2c,libfftw3f), Cvoid,
482+
ccall((:fftwf_execute_dft_r2c,libfftw3f[]), Cvoid,
483483
(PlanPtr,Ptr{Float32},Ptr{Complex{Float32}}), plan, X, Y)
484484

485485
unsafe_execute!(plan::rFFTWPlan{Complex{Float64},BACKWARD},
486486
X::StridedArray{Complex{Float64}}, Y::StridedArray{Float64}) =
487-
ccall((:fftw_execute_dft_c2r,libfftw3), Cvoid,
487+
ccall((:fftw_execute_dft_c2r,libfftw3[]), Cvoid,
488488
(PlanPtr,Ptr{Complex{Float64}},Ptr{Float64}), plan, X, Y)
489489

490490
unsafe_execute!(plan::rFFTWPlan{Complex{Float32},BACKWARD},
491491
X::StridedArray{Complex{Float32}}, Y::StridedArray{Float32}) =
492-
ccall((:fftwf_execute_dft_c2r,libfftw3f), Cvoid,
492+
ccall((:fftwf_execute_dft_c2r,libfftw3f[]), Cvoid,
493493
(PlanPtr,Ptr{Complex{Float32}},Ptr{Float32}), plan, X, Y)
494494

495495
unsafe_execute!(plan::r2rFFTWPlan{T},
496496
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} =
497-
ccall((:fftw_execute_r2r,libfftw3), Cvoid,
497+
ccall((:fftw_execute_r2r,libfftw3[]), Cvoid,
498498
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
499499

500500
unsafe_execute!(plan::r2rFFTWPlan{T},
501501
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
502-
ccall((:fftwf_execute_r2r,libfftw3f), Cvoid,
502+
ccall((:fftwf_execute_r2r,libfftw3f[]), Cvoid,
503503
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
504504

505505
# NOTE ON GC (garbage collection):
@@ -565,7 +565,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
565565
unsafe_set_timelimit($Tr, timelimit)
566566
R = isa(region, Tuple) ? region : copy(region)
567567
dims, howmany = dims_howmany(X, Y, [size(X)...], R)
568-
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib),
568+
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib[]),
569569
PlanPtr,
570570
(Int32, Ptr{Int}, Int32, Ptr{Int},
571571
Ptr{$Tc}, Ptr{$Tc}, Int32, UInt32),
@@ -585,7 +585,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
585585
region = circshift(Int[region...],-1) # FFTW halves last dim
586586
unsafe_set_timelimit($Tr, timelimit)
587587
dims, howmany = dims_howmany(X, Y, [size(X)...], region)
588-
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib),
588+
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib[]),
589589
PlanPtr,
590590
(Int32, Ptr{Int}, Int32, Ptr{Int},
591591
Ptr{$Tr}, Ptr{$Tc}, UInt32),
@@ -605,7 +605,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
605605
region = circshift(Int[region...],-1) # FFTW halves last dim
606606
unsafe_set_timelimit($Tr, timelimit)
607607
dims, howmany = dims_howmany(X, Y, [size(Y)...], region)
608-
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib),
608+
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib[]),
609609
PlanPtr,
610610
(Int32, Ptr{Int}, Int32, Ptr{Int},
611611
Ptr{$Tc}, Ptr{$Tr}, UInt32),
@@ -626,7 +626,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
626626
knd = fix_kinds(region, kinds)
627627
unsafe_set_timelimit($Tr, timelimit)
628628
dims, howmany = dims_howmany(X, Y, [size(X)...], region)
629-
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
629+
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib[]),
630630
PlanPtr,
631631
(Int32, Ptr{Int}, Int32, Ptr{Int},
632632
Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, UInt32),
@@ -651,7 +651,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
651651
dims[2:3, 1:size(dims,2)] *= 2
652652
howmany[2:3, 1:size(howmany,2)] *= 2
653653
howmany = [howmany [2,1,1]] # append loop over real/imag parts
654-
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
654+
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib[]),
655655
PlanPtr,
656656
(Int32, Ptr{Int}, Int32, Ptr{Int},
657657
Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, UInt32),

src/providers.jl

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ end
2020
# Read in preferences, see if any users have requested a particular backend
2121
const fftw_provider = get_provider()
2222

23+
# We'll initialize `libfftw3` here (in the conditionals below), and
24+
# it will get overwritten again in `__init__()`. This allows us to
25+
# `ccall` at build time, and also be relocatable for PackageCompiler.
26+
const libfftw3 = Ref{String}()
27+
const libfftw3f = Ref{String}()
28+
2329
"""
2430
set_provider!(provider; export_prefs::Bool = false)
2531
@@ -41,7 +47,9 @@ end
4147

4248
# If we're using fftw_jll, load it in
4349
@static if fftw_provider == "fftw"
44-
using FFTW_jll
50+
import FFTW_jll
51+
libfftw3[] = FFTW_jll.libfftw3_path
52+
libfftw3f[] = FFTW_jll.libfftw3f_path
4553

4654
# callback function that FFTW uses to launch `num` parallel
4755
# tasks (FFTW/fftw3#175):
@@ -58,23 +66,23 @@ end
5866
# (Previously, we called fftw_cleanup, but this invalidated existing
5967
# plans, causing Base Julia issue #19892.)
6068
function fftw_init_threads()
61-
stat = ccall((:fftw_init_threads, libfftw3), Int32, ())
62-
statf = ccall((:fftwf_init_threads, libfftw3f), Int32, ())
69+
stat = ccall((:fftw_init_threads, libfftw3[]), Int32, ())
70+
statf = ccall((:fftwf_init_threads, libfftw3f[]), Int32, ())
6371
if stat == 0 || statf == 0
6472
error("could not initialize FFTW threads")
6573
end
6674

6775
if nthreads() > 1
6876
cspawnloop = @cfunction(spawnloop, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Cint, Ptr{Cvoid}))
69-
ccall((:fftw_threads_set_callback, libfftw3), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL)
70-
ccall((:fftwf_threads_set_callback, libfftw3f), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL)
77+
ccall((:fftw_threads_set_callback, libfftw3[]), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL)
78+
ccall((:fftwf_threads_set_callback, libfftw3f[]), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL)
7179
end
7280
end
7381
end
7482

7583
# If we're using MKL, load it in and set library paths appropriately.
7684
@static if fftw_provider == "mkl"
77-
using MKL_jll
78-
const libfftw3 = MKL_jll.libmkl_rt_path
79-
const libfftw3f = libfftw3
85+
import MKL_jll
86+
libfftw3[] = MKL_jll.libmkl_rt_path
87+
libfftw3f[] = MKL_jll.libmkl_rt_path
8088
end

test/runtests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ end # fftw_provider == "fftw"
507507
end
508508

509509
# check whether FFTW on this architecture has nontrivial alignment requirements
510-
nontrivial_alignment = FFTW.fftw_provider == "fftw" && ccall((:fftwf_alignment_of, FFTW.libfftw3f), Int32, (Int,), 8) != 0
510+
nontrivial_alignment = FFTW.fftw_provider == "fftw" && ccall((:fftwf_alignment_of, FFTW.libfftw3f[]), Int32, (Int,), 8) != 0
511511
if nontrivial_alignment
512512
@test_throws ArgumentError plan_rfft(Array{Float32}(undef, 32)) * view(A, 2:33)
513513
@test_throws ArgumentError plan_fft(Array{Complex{Float32}}(undef, 32)) * view(Ac, 2:33)

0 commit comments

Comments
 (0)