LAPACK function not found

In rewriting fortran code I found it useful to call LAPACK code directly.

function dgetrf!(m::IT, n::IT, A::SubArray{FT, 1, Vector{FT}, Tuple{UnitRange{IT}}, true}, lda::IT, ipiv::SubArray{IT, 1, Vector{IT}, Tuple{UnitRange{IT}}, true}) where {IT, FT}
    A = rand(7, 7)
    @show A, ipiv, info = getrf!(A)
    info = Ref{LinearAlgebra.BLAS.BlasInt}()
    ccall((LinearAlgebra.LAPACK.@blasfunc(:dgetrf_), LinearAlgebra.LAPACK.libblastrampoline), Cvoid,
        (Ref{LinearAlgebra.BLAS.BlasInt}, Ref{LinearAlgebra.BLAS.BlasInt}, Ptr{FT},
            Ref{LinearAlgebra.BLAS.BlasInt}, Ptr{LinearAlgebra.BLAS.BlasInt}, Ptr{LinearAlgebra.BLAS.BlasInt}),
        m, n, A, lda, ipiv, info)
    chkargsok(info[])
    return info[] #Error code is stored in LU factorization type
end

Unfortunately, I get this error:

  LoadError: could not load symbol ":dgetrf_64_":                             
  The specified procedure could not be found.                                 
  Stacktrace:                                                                 
    [1] dgetrf!(m::Int64, n::Int64, A::SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}, lda::Int64, ipiv::SubArray{Int64, 1, Vector{Int64}, Tuple{UnitRange{Int64}}, true})                                          
      @ Sparspak.SpkSpdMMops C:\Users\pkonl\Documents\00WIP\Sparspak.jl\src\SparseSpdMethod\SpkSpdMMops.jl:333    

This is sure curious, because I call getrf! directly as a test just above the ccall, and that works.
Any ideas where to look for the catch?

I found that @blasfunc generated some extra characters. Without those, the call will go through:

julia> dlsym(h, LinearAlgebra.LAPACK.@blasfunc(:dgetrf_))
ERROR: could not load symbol ":dgetrf_64_":
The specified procedure could not be found.
Stacktrace:
 [1] #dlsym#1
   @ .\libdl.jl:59 [inlined]
 [2] dlsym(hnd::Ptr{Nothing}, s::Symbol)
   @ Base.Libc.Libdl .\libdl.jl:57
 [3] top-level scope
   @ REPL[17]:1

On the other hand, hard-coding the name works:

julia> dlsym(h, :dgetrf_)
Ptr{Nothing} @0x0000000065e0c0d4

I wonder why that is? I am using 64-bit machines, but apparently that is not right. The macro is defined as

if USE_BLAS64
    macro blasfunc(x)
        return Expr(:quote, Symbol(x, "64_"))
    end
else
    macro blasfunc(x)
        return Expr(:quote, x)
    end
end

Which, correctly I think, deduces that mine is a 64-bit machine.

julia> versioninfo()
Julia Version 1.7.2
Commit bf53498635 (2022-02-06 15:21 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Intel(R) Core(TM) i5-7300U CPU @ 2.60GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-12.0.1 (ORCJIT, skylake)
Environment:
  JULIA_DEPOT_PATH = C:/Users/pkonl/VSCode_Julia_portable-main/assets/.julia-1.7.2-depot
  JULIA_EDITOR = code
  JULIA_NUM_THREADS =

I would take a look at

and

I’m guessing the complication is that that on Windows we do not need ILP64?

See Make.inc and search for USE_BLAS64:

Precisely, that is what I did above (I think, unless you had something else in mind?).

Right, but then why does it work in getrf!, but not in my hand-crafted code?

Try
LinearAlgebra.LAPACK.@blasfunc(dgetrf_)

IIUC, In the context of a macro, the token is already a symbol so the extra colon is not wanted.

Thanks. But, I’m confused: The code to generate the functions starts with

so that in my understanding it is the symbols that are passed to the @blasfunc macro, for instance

So rewriting by hand

ccall((@blasfunc(:gebrd_), 

Where is my mistake?

Edit: Sorry, I get it now.
Edit, edit: Actually, I don’t. :frowning:

In the stdlib case, the symbol is interpolated into the argument of the @eval macro and somehow becomes an ordinary token again. I admit to finding the documentation for this situation confusing, and to resorting to trial and error in cases like this.

1 Like

Here are my results:

julia> using Libdl

julia> import LinearAlgebra.BLAS: @blasfunc

julia> h = dlopen(LinearAlgebra.LAPACK.libblastrampoline)
Ptr{Nothing} @0x0000000065e00000

julia> @blasfunc(dgetrf_)
:dgetrf_64_

julia> dlsym(h, @blasfunc(dgetrf_))
Ptr{Nothing} @0x0000000065e1a8a0

julia> dlsym(h, :dgetrf_64_)
Ptr{Nothing} @0x0000000065e1a8a0

julia> dlsym(h, :dgetrf_)
Ptr{Nothing} @0x0000000065e0c0d4

I think you were getting yourself confused about interpolation within the @eval macro as Ralph mentioned.

julia> sym = :dgetrf_
:dgetrf_

julia> @blasfunc(dgetrf_)
:dgetrf_64_

julia> @eval begin
           @blasfunc($sym)
       end
:dgetrf_64_

julia> @blasfunc(:dgetrf_)
Symbol(":dgetrf_64_")
1 Like