Skip to content

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

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

SET_XERBLA - override the error-handler without linker stuff#1407
mohawk2 wants to merge 10 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 29.57746% with 200 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.36%. Comparing base (a6c6e74) to head (4acfc83).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
BLAS/SRC/xerbla_blas.f90 81.25% 2 Missing and 1 partial ⚠️
SRC/xerbla_lapack.f90 85.00% 2 Missing and 1 partial ⚠️
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     6124    +2     
  Lines      486711   486783   +72     
  Branches    23268    23270    +2     
=======================================
+ Hits       337584   337654   +70     
  Misses     148689   148689           
- Partials      438      440    +2     
Components Coverage Δ
BLAS 97.93% <85.00%> (-0.01%) ⬇️
CBLAS 96.98% <ø> (ø)
LAPACK 82.39% <87.50%> (+<0.01%) ⬆️
LAPACKE 2.17% <ø> (ø)
TMGLIB 55.69% <ø> (ø)
BLAS testing 88.34% <100.00%> (+<0.01%) ⬆️
CBLAS testing 89.63% <ø> (ø)
LAPACK testing 82.21% <13.00%> (+<0.01%) ⬆️
LAPACKE testing ∅ <ø> (∅)
Files with missing lines Coverage Δ
BLAS/SRC/xerbla.f 75.00% <100.00%> (+75.00%) ⬆️
BLAS/TESTING/cblat2.f 85.47% <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%) ⬆️
SRC/xerbla.f 75.00% <100.00%> (+75.00%) ⬆️
... and 196 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 a6c6e74...4acfc83. 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_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 (see https://blogs.perl.org/users/ed_j/2026/09/adventures-with-fortran.html).
Edit 5: Moved prototype code to a blog post.
Edit 6: Removed BLAS SET_XERBLA as would create dependency on LAPACK.

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 to after the verb.

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
@mohawk2
mohawk2 force-pushed the register-xerbla branch 3 times, most recently from 149c4ce to 2449269 Compare September 13, 2026 07:06
@mohawk2

mohawk2 commented Sep 13, 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 suggestion would be to have the separate symbols and then one symbol that does both and lives in LAPACK. Something like this:
[...]
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.

@ACSimon33 I've had to adjust the plan a bit; a BLAS SET_XERBLA would have a dependency on LAPACK, which I cannot see as a good thing, so I've removed that. However there is now in the latest push an LAPACK SET_XERBLA which seems to work fine.

@ACSimon33

Copy link
Copy Markdown
Collaborator

@mohawk2 Can you please merge the current master back into your branch? We now fixed numerical issues and errors on all platforms that are tested in CI, and the CI jobs are now configured to fail if new errors occur.

langou
langou previously approved these changes Sep 13, 2026
@mohawk2

mohawk2 commented Sep 13, 2026

Copy link
Copy Markdown
Author

@mohawk2 Can you please merge the current master back into your branch? We now fixed numerical issues and errors on all platforms that are tested in CI, and the CI jobs are now configured to fail if new errors occur.

Working on this now, thank you for highlighting this.

@mohawk2

mohawk2 commented Sep 13, 2026

Copy link
Copy Markdown
Author

@ACSimon33 @langou I've just rebased this to latest master and force-pushed; it passes all the tests on my local device. It doesn't have the probe for a compliant BLAS yet, nor CBLAS/LAPACKE implementations, which I'll be working on now, but I believe it is ready for merging as-is.

@mohawk2

mohawk2 commented Sep 13, 2026

Copy link
Copy Markdown
Author

@ACSimon33 @langou Sorry about this! I'm pretty sure the Makefile jobs in CI will fail, because I missed updating the Makefiles to add the 2 new files due to inept git grep. Will add another commit in a few moments.

@mohawk2

mohawk2 commented Sep 13, 2026

Copy link
Copy Markdown
Author

@ACSimon33 @langou Pushed. Works locally (and confirmed that without the new commit, it broke).

@mohawk2

mohawk2 commented Sep 13, 2026

Copy link
Copy Markdown
Author

Flang is failing with:

error: loc("/home/runner/work/lapack/lapack/BLAS/SRC/xerbla_blas.f90":74:47): 'hlfir.declare' op operand #0 must be any reference or box, but got '!fir.boxproc<() -> ()>'
error: loc("/home/runner/work/lapack/lapack/BLAS/SRC/xerbla_blas.f90":74:47): 'hlfir.declare' op operand #0 must be any reference or box, but got '!fir.boxproc<() -> ()>'
error: loc("/home/runner/work/lapack/lapack/BLAS/SRC/xerbla_blas.f90":74:47): 'hlfir.declare' op operand #0 must be any reference or box, but got '!fir.boxproc<() -> ()>'

Some preliminary searching indicates this is because it doesn't like the combination of procedure pointers, and ENTRY. I've stuck with ENTRY because the _64 generator doesn't handle modules right. I can make it do so; it's extra work but I don't mind.

@ACSimon33 How do you want to proceed? If you can't accept breaking flang, which I get, please say so and I'll make a small sub-PR of this work so far with the 2 Generate* improvements and the noise-reduction commit that changes the test xerblas to calling their own xer_replace.

Edit: also codecov is saying it's failing because this part doesn't get exercised:

   if (.not. associated(active_callback)) then
     print *, 'Error: LAPACK_XERBLA called but no callback registered'
     stop
   end if

I had that in there because I thought it was better than a SEGV. I'm not sure how to proceed there.

@mohawk2

mohawk2 commented Sep 13, 2026

Copy link
Copy Markdown
Author

@ACSimon33 (sorry for all the highlights) I have opened #1411 further to my last comment, and will get started on instead using module and enhancing the Generate to handle that.

@ACSimon33

ACSimon33 commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

@mohawk2 Try removing the GET_ entries. They seem to be the problem. It builds fine for me without them.

@mohawk2

mohawk2 commented Sep 13, 2026

Copy link
Copy Markdown
Author

@mohawk2 Try removing the GET_ routines. They seem to be the problem. It builds fine for me without them.

@ACSimon33 I'm a bit reluctant! That would break returning the previous handlers on the C side. I'm comfortable with adjusting the Generate stuff; one consequence is it will suffix the la_isnan which it currently doesn't, though if that's undesirable I can special-case out la_*, as I'll already need to special-case iso_* modules.

Edit: clarify tangled wording.

Further info: the flang that is breaking is 21.1.8 on Ubuntu 26.04. On my local flang 23.1.1, and the Arm-distributed one (which may well be the same version), all is well.

@ACSimon33

Copy link
Copy Markdown
Collaborator

@mohawk2 Try removing the GET_ routines. They seem to be the problem. It builds fine for me without them.

@ACSimon33 I'm a bit reluctant! That would break returning the previous handlers on the C side. I'm comfortable with adjusting the Generate stuff; one consequence is it will suffix the la_isnan which it currently doesn't, though if that's undesirable I can special-case out la_*, as I'll already need to special-case iso_* modules.

Edit: clarify tangled wording.

Further info: the flang that is breaking is 21.1.8 on Ubuntu 26.04. On my local flang 23.1.1, and the Arm-distributed one (which may well be the same version), all is well.

Right, we need it there. I don't have a specific preference about how we fix this, since I'm not a Fortran expert. Using a C function pointer with an internal indirection would work like this, I think:

  entry blas_xerbla_handler_cptr(cb_ptr)      ! type(c_funptr) -> flang-safe
    if (associated(active_callback)) then
      cb_ptr = c_funloc(active_callback)
    else
      cb_ptr = c_null_funptr
    end if
  return
  ...
end

subroutine get_blas_xerbla(cb_ret)            ! no ENTRY siblings
  procedure(), pointer :: cb_ret
  type(c_funptr)     :: cb_ptr
  external           blas_xerbla_handler_cptr
  call blas_xerbla_handler_cptr(cb_ptr)
  if (c_associated(cb_ptr)) then
    call c_f_procpointer(cb_ptr, cb_ret)
  else
    cb_ret => null()
  end if
end

But as I said, I'm fine with either way.

@mohawk2

mohawk2 commented Sep 13, 2026

Copy link
Copy Markdown
Author

Right, we need it there. I don't have a specific preference about how we fix this, since I'm not a Fortran expert. Using a C function pointer with an internal indirection would work like this, I think:

[some sorcery]

But as I said, I'm fine with either way.

@ACSimon33 Great, I'm already figuring how to wend my way through the logic and regex stuff in the CMake code. Please could you look at #1411? That would really help.

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.

4 participants