Skip to content

SET_XERBLA - override the error-handler without linker stuff - #1407

Open
mohawk2 wants to merge 8 commits into
Reference-LAPACK:masterfrom
mohawk2:register-xerbla
Open

SET_XERBLA - override the error-handler without linker stuff#1407
mohawk2 wants to merge 8 commits into
Reference-LAPACK:masterfrom
mohawk2:register-xerbla

Conversation

@mohawk2

@mohawk2 mohawk2 commented Sep 11, 2026

Copy link
Copy Markdown

Description

Currently, to override BLAS/LAPACK's handling of errors involves defining a xerbla_ (or sometimes xerbla) symbol in your library/executable. This may not work in 2-level namespace environments like macOS.

Instead, this PR allows you to call SET_XERBLA (Fortran) with a replacement handler, or NULL() to reset to default. There is also an GET_(library)_XERBLA. This does not address that the new handler must be a Fortran-compatible routine.

There are CBLAS (cblas_set_xerbla) and LAPACKE (LAPACKE_set_xerbla) handlers on the way, but I'm putting this up for review as I believe it already adds value.

I have marked below that the documentation has been updated, because I added a paragraph in the doc-comments of xerbla.f. If more is needed, please let me know.

EDIT: updated function names.

Checklist

  • The documentation has been updated.
  • If the PR solves a specific issue, it is set to be closed on merge.

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 21.87500% with 200 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.36%. Comparing base (9eaccc1) to head (59116dd).
⚠️ Report is 3 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
BLAS/SRC/xerbla.f 66.66% 3 Missing ⚠️
SRC/xerbla.f 66.66% 3 Missing ⚠️
TESTING/LIN/cqrt14.f 0.00% 3 Missing ⚠️
TESTING/LIN/cqrt15.f 0.00% 3 Missing ⚠️
TESTING/LIN/dqrt14.f 0.00% 3 Missing ⚠️
TESTING/LIN/dqrt15.f 0.00% 3 Missing ⚠️
TESTING/LIN/sqrt14.f 0.00% 3 Missing ⚠️
TESTING/LIN/sqrt15.f 0.00% 3 Missing ⚠️
TESTING/LIN/zqrt14.f 0.00% 3 Missing ⚠️
TESTING/LIN/zqrt15.f 0.00% 3 Missing ⚠️
... and 158 more
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1407   +/-   ##
=======================================
  Coverage   69.36%   69.36%           
=======================================
  Files        6122     6122           
  Lines      486337   486381   +44     
  Branches    23268    23268           
=======================================
+ Hits       337330   337372   +42     
- Misses     148569   148571    +2     
  Partials      438      438           
Components Coverage Δ
BLAS 97.93% <66.66%> (-0.01%) ⬇️
CBLAS 96.98% <ø> (ø)
LAPACK 82.38% <66.66%> (+<0.01%) ⬆️
LAPACKE 2.17% <ø> (ø)
TMGLIB 55.69% <ø> (ø)
BLAS testing 88.34% <100.00%> (+<0.01%) ⬆️
CBLAS testing 89.63% <ø> (ø)
LAPACK testing 82.24% <12.61%> (+<0.01%) ⬆️
LAPACKE testing ∅ <ø> (∅)
Files with missing lines Coverage Δ
BLAS/TESTING/cblat2.f 85.46% <100.00%> (+<0.01%) ⬆️
BLAS/TESTING/cblat3.f 90.97% <100.00%> (+<0.01%) ⬆️
BLAS/TESTING/dblat2.f 85.38% <100.00%> (+<0.01%) ⬆️
BLAS/TESTING/dblat3.f 89.45% <100.00%> (+<0.01%) ⬆️
BLAS/TESTING/sblat2.f 85.38% <100.00%> (+<0.01%) ⬆️
BLAS/TESTING/sblat3.f 89.45% <100.00%> (+<0.01%) ⬆️
BLAS/TESTING/zblat2.f 85.46% <100.00%> (+<0.01%) ⬆️
BLAS/TESTING/zblat3.f 90.97% <100.00%> (+<0.01%) ⬆️
TESTING/EIG/cchkdmd.f90 75.08% <100.00%> (+0.08%) ⬆️
TESTING/EIG/cchkee.F 80.00% <100.00%> (+0.02%) ⬆️
... and 194 more

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9eaccc1...59116dd. Read the comment docs.

@ACSimon33

ACSimon33 commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Hi @mohawk2,

I like the idea a lot, since it would solve all the problems we currently have with error testing (e.g., it doesn't work with shared Windows libs or two-level namespaces) and make it easier for users to use a custom xerbla on all platforms.

A few things to consider:

  • Naming: There are precedents from other BLAS/LAPACK libraries (openblas_set_xerbla, mkl_set_xerbla). Also, it might make sense to split the function into two versions, one for BLAS and one for LAPACK, like this: BLAS_SET_XERBLA and LAPACK_SET_XERBLA (or with REGISTER instead of SET). That would cause fewer name symbol clashes, and the user could decide which error handler to replace.
  • Return pointers: openblas_set_xerbla and mkl_set_xerbla return the previous xerbla function pointer. I'm not sure yet how easy that would be to replicate when implementing it in Fortran instead of C...
  • Testing optimized libs: Linking the testing framework with optimized BLAS/LAPACK would not work until vendors implement it. Not really an issue, since if they want to use the reference test suite, they should implement the missing functions.
  • CBLAS/LAPACKE: It would be nice to have corresponding CBLAS and LAPACKE versions that set their xerbla functions. I don't think anything in the PR makes that difficult, so this can be a follow-up discussion and PR.

@ilayn

ilayn commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

There is also a possibility of defining it as a weak symbol so tests can override the regular one. that can limit the surgery to only xerbla. overall I don't think xerbla is a good way to report errors back but it is what it is for now.

@ACSimon33

Copy link
Copy Markdown
Collaborator

There is also a possibility of defining it as a weak symbol so tests can override the regular one. that can limit the surgery to only xerbla. overall I don't think xerbla is a good way to report errors back but it is what it is for now.

Yes, that's how it's currently done in the CBLAS test suite. Won't work on Windows, though. Also, doing that in Fortran would depend on the compilers supporting it (nagfor doesn't without major hacks, ifx & flang maybe).

@ilayn

ilayn commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

I do use it on windows albeit on a C translation and Clang.

Also, doing that in Fortran would depend on the compilers supporting it (nagfor doesn't without major hacks, ifx & flang maybe).

ah then nevermind. Sorry for the noise

@ACSimon33

Copy link
Copy Markdown
Collaborator

I do use it on windows albeit on a C translation and Clang.

Ah, you're right; Clang and Intel do support it on Windows for static libraries. Did you try it with a shared library? Because last time I checked, that was the blocker.

@mohawk2

mohawk2 commented Sep 11, 2026

Copy link
Copy Markdown
Author

Thank you all for your quick responses! As noted in my edit of the PR description, I'm intending to add CBLAS and LAPACKE equivalents. I'll ponder the names, I hadn't properly checked prior art and will do so now.

I'll ponder the idea of different BLAS and LAPACK handlers - is it really true that the current implementation essentially has two similarly-named symbols that are each are getting overridden by the user-supplied one? If so, that seems horrible. I might need to make differently-named entry points for each that call each other, since the user expectation will be a full override for both.

@ACSimon33

ACSimon33 commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

I'll ponder the idea of different BLAS and LAPACK handlers - is it really true that the current implementation essentially has two similarly-named symbols that are each are getting overridden by the user-supplied one? If so, that seems horrible. I might need to make differently-named entry points for each that call each other, since the user expectation will be a full override for both.

Yes, I think using the same REGISTER_XERBLA symbol name in BLAS and LAPACK could mean it only replaces one of the handlers (if we use separate shared libraries). My sugestion would be to have the separate symbols and then one symbol that does both and lives in LAPACK. Something like this:

    BLAS_SET_XERBLA      ->  libblas.so
    LAPACK_SET_XERBLA    ->  liblapack.so
    SET_XERBLA           ->  liblapack.so (calls the other two)
    
    BLAS_RESET_XERBLA    ->  libblas.so
    LAPACK_RESET_XERBLA  ->  liblapack.so
    RESET_XERBLA         ->  liblapack.so (calls the other two)

Edit:

One issue would be that when we are compiling LAPACK with a vendor BLAS lib and they don't implement BLAS_SET_XERBLA yet, SET_XERBLA would call an undefined symbol. So we would need to check if the supplied vendor BLAS contains the symbol and, if not, disable the call via a preprocessor define.

@mohawk2

mohawk2 commented Sep 11, 2026

Copy link
Copy Markdown
Author

Yes, I think using the same REGISTER_XERBLA symbol name in BLAS and LAPACK could mean it only replaces one of the handlers (if we use separate shared libraries). My sugestion would be to have the separate symbols and then one symbol that does both and lives in LAPACK. Something like this:

    BLAS_SET_XERBLA      ->  libblas.so
    LAPACK_SET_XERBLA    ->  liblapack.so
    SET_XERBLA           ->  liblapack.so (calls the other two)
    
    BLAS_RESET_XERBLA    ->  libblas.so
    LAPACK_RESET_XERBLA  ->  liblapack.so
    RESET_XERBLA         ->  liblapack.so (calls the other two)

My pondering came up with a similar result, but not identical (see below). The above discussion included the point that prior art (openblas_set_xerbla, where Google tried real hard to persuade me I really meant openblas set_xlabel) returns the previously-set handler. That's fine in C, where you can just ignore a return value, but (I looked it up, but felt I should confirm it) in Fortran you cannot ignore return values, which would render code using the SET function quite ugly. Also, having two separate values would render a function (which must return one value) unworkable. I have come up with this:

Library Name Type Purpose
BLAS SET_XERBLA Subroutine Calls SET_BLAS_XERBLA and SET_LAPACK_XERBLA
BLAS SET_BLAS_XERBLA Subroutine Sets BLAS one; use NULL() value to get default
BLAS GET_BLAS_XERBLA Subroutine Returns current BLAS one in a return param
LAPACK SET_XERBLA Subroutine Calls SET_BLAS_XERBLA and SET_LAPACK_XERBLA
LAPACK SET_LAPACK_XERBLA Subroutine Sets LAPACK one; use NULL() value to get default
LAPACK GET_LAPACK_XERBLA Subroutine Returns current LAPACK one in a return param

Edit 1: specify that NULL() gives default behaviour in SET_*.
Edit 2: rename so SET_/GET_ is at start, to avoid cognitive load in CBLAS/LAPACKE versions.
Edit 3: GET_ must be a subroutine because no mixing ENTRY types (ref, plus I tried it).
Edit 4: Prototype code that works:

subroutine xerbla(srname, info)
  character*(*), intent(in) :: srname
  integer, intent(in) :: info
  procedure(xerbla_interface), pointer :: active_callback => null(), cb_ret
  procedure(xerbla_interface) :: cb
  abstract interface
    subroutine xerbla_interface(srname, info)
      character*(*), intent(in) :: srname
      integer, intent(in) :: info
    end subroutine
  end interface
  if (associated(active_callback)) then
    call active_callback(srname, info)
  else
    print *, 'I am the main xerbla'
  end if
  return
  entry set_xerbla(cb)
    active_callback => cb
  return
  entry get_xerbla(cb_ret)
    cb_ret => active_callback
  return
end subroutine

subroutine xerbla_replacement(srname, info)
  character*(*), intent(in) :: srname
  integer, intent(in) :: info
  print *, 'I am the replacement xerbla'
end subroutine

program hello
  implicit none
  external xerbla_replacement, set_xerbla, xerbla
  procedure(), pointer :: already_cb
  interface
    subroutine get_xerbla(cb_ret)
      procedure(), pointer :: cb_ret
    end subroutine
  end interface
  call xerbla('Hello, World!', 5)
  call get_xerbla(already_cb)
  call set_xerbla(xerbla_replacement)
  call xerbla('Hello, World!', 5)
  call set_xerbla(already_cb)
  call xerbla('Hello, World!', 5)
end program hello

Prototype code using a module that doesn't work in current LAPACK because the _64 code doesn't get use and module statements right:

module xerbla_callbacks
  implicit none
  private
  public :: active_callback, xerbla_interface
  procedure(xerbla_interface), pointer :: active_callback => null()
  interface
    subroutine xerbla_interface(srname, info)
      character*(*), intent(in) :: srname
      integer, intent(in) :: info
    end subroutine
  end interface
end module xerbla_callbacks

subroutine set_xerbla(cb)
  use xerbla_callbacks
  implicit none
  procedure() :: cb
  active_callback => cb
end subroutine set_xerbla

subroutine get_xerbla(cb_ret)
  use xerbla_callbacks
  procedure(xerbla_interface), pointer :: cb_ret
  cb_ret => active_callback
end subroutine get_xerbla

subroutine xerbla(srname, info)
  use xerbla_callbacks
  character*(*), intent(in) :: srname
  integer, intent(in) :: info
  if (associated(active_callback)) then
    call active_callback(srname, info)
  else
    print *, 'I am the main xerbla'
  end if
end subroutine

subroutine xerbla_replacement(srname, info)
  character*(*), intent(in) :: srname
  integer, intent(in) :: info
  print *, 'I am the replacement xerbla'
end subroutine

program hello
  use xerbla_callbacks
  implicit none
  external xerbla_replacement, set_xerbla, xerbla
  procedure(), pointer :: already_cb
  interface
    subroutine get_xerbla(cb_ret)
      procedure(), pointer :: cb_ret
    end subroutine
  end interface
  call xerbla('Hello, World!', 5)
  call get_xerbla(already_cb)
  call set_xerbla(xerbla_replacement)
  call xerbla('Hello, World!', 5)
  call set_xerbla(already_cb)
  call xerbla('Hello, World!', 5)
end program hello

One issue would be that when we are compiling LAPACK with a vendor BLAS lib and they don't implement BLAS_SET_XERBLA yet, SET_XERBLA would call an undefined symbol. So we would need to check if the supplied vendor BLAS contains the symbol and, if not, disable the call via a preprocessor define.

Yes, that sounds like a good approach.

@mohawk2

mohawk2 commented Sep 11, 2026

Copy link
Copy Markdown
Author

My thinking so far on a CBLAS approach (The LAPACKE version will be mutatis mutandis, so will not be addressed separately). CBLAS has an actual xerbla_ replacement with C-orientated enhancements, which I'd need to update to have the same as each new respective xerbla.f part, so that if Fortran code linked with our Brave New BLAS called set_xerbla etc, its expectations would be met.

cblas_set_xerbla reveals a close-to-ambiguity, so I've updated the above to move the library name portion of the procedures above.

For the CBLAS versions of the 3 procedures plus cblas_xerbla, the handler interface will be the same as cblas_xerbla (including varargs stuff). Other notes:

Function Return value Behaviour
cblas_set_xerbla Previous value of CBLAS handler, like openblas_set_xerbla Calls cblas_set_blas_xerbla and LAPACKE_set_lapack_xerbla
cblas_get_blas_xerbla Current CBLAS handler Return current
cblas_set_blas_xerbla Current CBLAS handler Will have a xerbla_ wrapper to pass to SET_BLAS_XERBLA_, that will refer internally to a callback. It will need to call SET_BLAS_XERBLA_ explicitly with a callback since we won't now rely on the symbol override, and it might not have worked. Details will become clearer on implementation.
cblas_xerbla void Will be updated like xerbla_ to dispatch on whether the stored callback pointer is NULL.

@mohawk2 mohawk2 changed the title REGISTER_XERBLA - override the error-handler without linker stuff SET_XERBLA - override the error-handler without linker stuff Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants