Can NVHPC-bundled OpenBLAS LP64 and ILP64 LAPACK interfaces be used in one executable?

I am trying to support automatic LP64/ILP64 dispatch for LAPACK eigensolvers such as DSYEVD and ZHEEVD using the OpenBLAS libraries bundled with the NVIDIA HPC SDK.

My environment is:


NVIDIA HPC SDK: 26.3
Compiler: nvfortran
Platform: Linux x86_64

The installation contains separate libraries:


liblapack_lp64.so
liblapack_ilp64.so
libblas_lp64.so
libblas_ilp64.so

I expected that the LP64 library might provide both the ordinary and explicit 64-bit LAPACK entry points, similar to Intel MKL and NVPL:


call zheevd(...)       ! INTEGER(4)
call zheevd_64(...)    ! INTEGER(8)

However, inspecting the NVHPC libraries gives the following result:


nm -D liblapack_lp64.so  | grep -i zheevd
nm -D liblapack_ilp64.so | grep -i zheevd

Both libraries appear to export:


zheevd_

and neither appears to export:


zheevd_64_

I see the same behavior for DSYEVD.

At present, I understand the required usage to be:


LP64:
    INTEGER(4) LAPACK arguments
    call zheevd(...)
    link -llapack_lp64 -lblas_lp64

ILP64:
    INTEGER(8) LAPACK arguments
    call zheevd(...)
    link -llapack_ilp64 -lblas_ilp64

Because the LP64 and ILP64 libraries expose the same Fortran symbol names but use different integer ABIs, it appears that they cannot be linked into one ordinary executable and selected at runtime based on matrix size.

Could NVIDIA please clarify the following?

  1. Is this interpretation correct for the OpenBLAS libraries bundled with NVHPC?
  2. Must an application build separate LP64 and ILP64 executables or library variants?
  3. Is there any supported way to call both LP64 and ILP64 LAPACK interfaces from one executable?
  4. Does NVHPC provide alternate _64 symbols, wrappers, compiler options, or symbol-suffixed libraries that I have overlooked?
  5. What exactly does -Mmathlib=openblas select with respect to LP64 versus ILP64?
  6. Is there an NVHPC equivalent of the NVPL pattern in which an LP64 library also exports explicit _64 routines?

-Mmathlib=openblas will link -llapack_lp64 -lblas_lp64 for normal invocation and -llapack_ilp64 -lblas_ilp64 when there is a -i8 flag ( you can see the differences running with -v o -dryrun).

It is possible to do what you want but you need to use dlopen from C.

  1. Use a generic interface for zheevd in your Fortran code
  2. Use dlopen to create two different symbols from C

Full example attached

Zheevd.tar.gz (9.5 KB)

./zheevd_dual

Calling the 32-bit OpenBLAS LP64 interface…

LP64 result

Eigenvalues:

1 -0.5868745120

2 3.1126808534

3 5.4741936586

Eigenvectors (one vector per column):

vector 1:

( -0.45404595, -0.05554929)

( 0.11109858, 0.36589620)

( 0.80282851, 0.00000000)

vector 2:

( 0.18085106, 0.23346517)

( -0.46693034, -0.67408846)

( 0.49027310, 0.00000000)

vector 3:

( 0.81310219, -0.20593273)

( 0.41186546, 0.10828074)

( 0.33926194, -0.00000000)

Calling the 64-bit OpenBLAS ILP64 interface…

ILP64 result

Eigenvalues:

1 -0.5868745120

2 3.1126808534

3 5.4741936586

Eigenvectors (one vector per column):

vector 1:

( -0.45404595, -0.05554929)

( 0.11109858, 0.36589620)

( 0.80282851, 0.00000000)

vector 2:

( 0.18085106, 0.23346517)

( -0.46693034, -0.67408846)

( 0.49027310, 0.00000000)

vector 3:

( 0.81310219, -0.20593273)

( 0.41186546, 0.10828074)

( 0.33926194, -0.00000000)

Maximum eigenvalue difference: 0.0000E+00

I will add to @mfatica 's response here - this is an interesting use case that we will have to think about more smooth solutions for in the future. In the past we have assumed a single precision type selection, but it makes sense with increasingly mixed precision approaches in various codes to have mechanisms for this. I’ll make sure to bring this up to our engineers and see what they think.

Thank you for the example. It confirms that both OpenBLAS interfaces can be used in one executable through dlopen/dlsym.

I have two follow-up questions about adapting this to my existing Fortran code.

I should clarify that my application is written in Fortran, not C. My existing implementation directly calls the native Fortran LAPACK routine:


call zheevd(jobz, uplo, n, a, lda, w,          &
            work, lwork, rwork, lrwork,         &
            iwork, liwork, info)

and explicitly performs the workspace query and allocation. The supplied example instead dynamically loads the high-level C interface:


LAPACKE_zheevd

which manages workspace internally.

  1. Is there a supported way to implement the same LP64/ILP64 runtime dispatch entirely from Fortran, without maintaining a separate C wrapper source file?
  2. Can dlopen/dlsym be used with the native LAPACK symbols:

zheevd_

from liblapack_lp64.so and liblapack_ilp64.so, rather than using LAPACKE_zheevd?

  1. If calling native zheevd_ through a C wrapper is supported, could you provide or describe the correct LP64 and ILP64 function-pointer declarations, including the handling of the Fortran CHARACTER*1 arguments JOBZ and UPLO and any hidden character-length arguments expected by the NVFORTRAN-built OpenBLAS libraries?
  2. Alternatively, would using:

LAPACKE_zheevd_work

be the recommended way to preserve caller-controlled workspace allocation while retaining the safer C interface?

My goal is to integrate this into existing Fortran modules where the workspace-query logic already exists, while keeping one executable that chooses LP64 or ILP64 according to the runtime matrix size.

This version will use the Fortran Lapack interface.

Zheevd_lapack.tar.gz (2.5 KB)

Just one small question:

What if I use the following instead of the interface provided by you in zheevd_dual.f90?

interface zheevd_openblas
subroutine zheevd_lp64(jobz, uplo, n, A, lda, W, work, lwork, rwork, lrwork, iwork, liwork, info)
character(len=1), intent(in) :: jobz, uplo
integer, intent(in) :: n, lda
complex(8), intent(inout) :: A()
real(8), intent(out) :: W(
)
complex(8), intent(inout) :: work()
integer, intent(in) :: lwork
real(8), intent(inout) :: rwork(
)
integer, intent(in) :: lrwork
integer, intent(inout) :: iwork()
integer, intent(in) :: liwork
integer, intent(out) :: info
end subroutine
subroutine zheevd_ilp64(jobz, uplo, n, A, lda, W, work, lwork, rwork, lrwork, iwork, liwork, info)
character(len=1), intent(in) :: jobz, uplo
integer(8), intent(in) :: n, lda
complex(8), intent(inout) :: A(
)
real(8), intent(out) :: W()
complex(8), intent(inout) :: work(
)
integer(8), intent(in) :: lwork
real(8), intent(inout) :: rwork()
integer(8), intent(in) :: lrwork
integer(8), intent(inout) :: iwork(
)
integer(8), intent(in) :: liwork
integer(8), intent(out) :: info
end subroutine
end interface

It should work. Once you have two different symbols, you can use the interface above.

Thanks a lot! I even tested the code by removing the entire interface and directly calling zheevd_lp64 and zheevd_ilp64. Even that worked! Thank you once again for your help.