Home

Awesome

<!--- MIT License CMake build script for the Accelerate LAPACKE project Copyright (c) 2024 Tim Kaune Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --->

Accelerate LAPACKE

Since MacOS 13.3 Ventura, Apple's Accelerate framework comes with a new BLAS/LAPACK interface compatible with Reference LAPACK v3.9.1. It also provides an ILP64 interface. On Apple Silicon M-processors, it utilises the proprietary AMX co-processor, which makes it especially interesting. Unfortunately, it comes without the LAPACKE C-interface library.

Update: With the release of MacOS 15.0 Sequoia, Apple updated the Accelerate framework to be compatible with Reference LAPACK v3.11.0. Unfortunately, there is no mention of it in the MacOS 15.0 Sequoia Release Notes, but the note in the Accelerate BLAS docs has been updated accordingly.

These new interfaces are hidden behind the preprocessor defines ACCELERATE_NEW_LAPACK and ACCELERATE_LAPACK_ILP64 and they only work, if you include the Accelerate C/C++ headers.

The Problem

But what if you have to or just want to link against the Accelerate framework without including the C/C++ headers, e. g. when compiling Fortran code or a third-party project, that uses the standard BLAS/LAPACK API? Well, you're out of luck. The binary symbols for the new LAPACK version exported by the Accelerate framework do not adhere to the BLAS/LAPACK API. Thus, they cannot be resolved by the linker, when linking any program or library that uses the standard BLAS/LAPACK API.

Take, for example, the dgeqrt LAPACK routine, that is used by the Reference LAPACK CMake script to determine, if the user provided LAPACK version is recent enough. When the Fortran test executable is compiled, the gfortran compiler creates a function call with the binary symbol _dgeqrt_, which results in the following error when linking to Accelerate (ld is the Apple system linker, here):

ld: Undefined symbols:
  _dgeqrt_, referenced from:
      _MAIN__ in testFortranCompiler.f.o

The reason for this is, that the binary symbol provided by the Accelerate framework is called _dgeqrt$NEWLAPACK, literally. This is a symbol, that no Fortran compiler will probably ever emit voluntarily. So, what to do?

The Solution

According to its man page, the Apple system linker ld provides the options -alias and -alias_list, which let you create alias names for existing binary symbols. Calling the linker with -alias '_dgeqrt$NEWLAPACK' _dgeqrt_ makes the linking of the above Fortran test executable finish successfully.

Because BLAS and LAPACK contain quite a number of subroutines and functions, this CMake scipt uses the -alias_list option, which loads a plaintext file listing all the aliases.

To generate the full alias list for the Accelerate NEWLAPACK interface, it parses the symbols listed in the BLAS and LAPACK text-based .dylib stubs. For every symbol that ends in $NEWLAPACK (or $NEWLAPACK$ILP64 for the ILP64 interface), an alias is added to the alias file.

The linker option is injected into the Reference LAPACK configure process using the CMAKE_EXE_LINKER_FLAGS variable for the test compiles and the CMAKE_SHARED_LINKER_FLAGS variable for the shared LAPACKE library.

This enables the compilation of the LAPACKE C-interface library for the Accelerate framework (e. g. to be used in the Eigen3 library). Analyzing the resulting .dylib with otool, you can see:

$ otool -L ./build/32/_deps/reference-lapack-build/lib/liblapacke.dylib
./build/32/_deps/reference-lapack-build/lib/liblapacke.dylib:
    @rpath/liblapacke.3.dylib (compatibility version 3.0.0, current version 3.9.1)
    /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate (compatibility version 1.0.0, current version 4.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.61.1)

Only the Accelerate framework and the System library are linked into the .dylib. No libgfortran or other libraries are needed.

The alias files (to use in other projects)

After the CMake configuration, the alias files can be found in ./build/<32|64>/src:

./build/<32|64>/src
├── new-lapack-ilp64.alias
└── new-lapack.alias

These files can be used to link other projects against Accelerate, too, of course!

How to compile

It is recommended to use the Apple System C Compiler /usr/bin/cc. You can also use a more recent Clang compiler provided by Homebrew or MacPorts. If you have other compilers installed on your system, make sure CMake finds the correct one. Otherwise, help CMake by setting the environment variable $ export CC=/usr/bin/cc in your terminal window.

It is also recommended to use at least CMake v3.20 with presets, but the CMake script also works down to CMake v3.12, if you set the required variables on the command line.

Prerequisites

Obviously, your operating system must be Mac OS X >=v13.3 Ventura with XCode installed. Additionally, you'll need the following software (easily obtainable via Homebrew or MacPorts):

Configuration with CMake

Use the accelerate-lapacke32 preset (or the accelerate-lapacke64 preset for the ILP64 interface) with CMake:

$ cmake --preset accelerate-lapacke32

This will configure LAPACKE to be installed in your ~/.local directory by default. If you prefer a different install location (e. g. /opt/custom), you can change it like this:

$ cmake --preset accelerate-lapacke32 -D "CMAKE_INSTALL_PREFIX=/opt/custom"

I wouldn't recommend installing to /usr/local (used by Homebrew on Intel Macs) or /opt/local (used by MacPorts).

Build and install

When the configuration finished successfully (fingers crossed), you can build with the same preset name (accelerate-lapacke32 or accelerate-lapacke64):

$ cmake --build --preset accelerate-lapacke32 --verbose

If everything worked as intended, linking the library should be successful. Now, you can install to the previously configured install prefix by building the install target:

$ cmake --build --preset accelerate-lapacke32 --verbose --target install

Using LAPACKE in another project

You can use your self-compiled LAPACKE library in other projects by importing the CMake package in the other project's CMakeLists.txt file:

find_package(LAPACKE CONFIG)

and providing the above install location via the CMAKE_PREFIX_PATH variable from the command line:

$ cmake -S . -B ./build -D "CMAKE_PREFIX_PATH=~/.local"

This makes the imported lapacke shared library target available in the other project's CMakeLists.txt.