Skip to content
Snippets Groups Projects
Unverified Commit 2456fbdd authored by Tim Kaune's avatar Tim Kaune
Browse files

Move parsing of alias files to external dependency Accelerate LAPACK

parent fc334bd7
No related branches found
No related tags found
No related merge requests found
......@@ -41,86 +41,16 @@ option(BUILD_INDEX64 "Link to ILP64 version of LAPACK?" OFF)
message(STATUS "Trying to link to ILP64 interface of LAPACK: ${BUILD_NEW_LAPACK}")
if (CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin")
# The available binary Accelerate version is determined by the MacOS system
message(STATUS "The Darwin kernel version is: ${CMAKE_HOST_SYSTEM_VERSION}")
if (CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 24.0)
# Mac OS X 15.0 Sequoia or later
message(STATUS "The MacOS version is: >=15.0")
set(MINIMUM_MACOS_SDK_VERSION 15.0)
set(ACCELERATE_NEW_LAPACK_VERSION 3.11.0)
elseif (CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 22.4)
# Mac OS X 13.3 Ventura or later
message(STATUS "The MacOS version is: >=13.3,<15.0")
set(MINIMUM_MACOS_SDK_VERSION 13.3)
set(VALID_MACOS_SDK_VERSIONS 14.5 14.4 14.2 14.0 13.3)
set(ACCELERATE_NEW_LAPACK_VERSION 3.9.1)
else ()
# Before Mac OS X 13.3 Ventura
message(FATAL_ERROR "You need at least MacOS 13.3 Ventura for Accelerate with BLAS/LAPACK v3.9.1!")
endif ()
message(STATUS "The BLAS/LAPACK version provided by the Accelerate framework should be: ${ACCELERATE_NEW_LAPACK_VERSION}")
else ()
message(FATAL_ERROR "Accelerate is only available on MacOS!")
endif ()
if (NOT DEFINED CMAKE_OSX_SYSROOT OR CMAKE_OSX_SYSROOT STREQUAL "")
message(FATAL_ERROR "Please provide the CMAKE_OSX_SYSROOT location! See <https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html>")
endif ()
string(REGEX MATCH [=[SDKs/MacOSX(.*)\.sdk$]=] _REGEX_DUMMY "${CMAKE_OSX_SYSROOT}")
set(MAIN_MACOS_SDK_VERSION "${CMAKE_MATCH_1}")
# The text-based .dylib stubs describing the Accelerate framework are provided
# by the MacOS SDK, which has to match the MacOS system. Otherwise, there might
# be newer symbols in the text-based .dylib stubs that are not provided by the
# Accelerate binary, leading to linking errors.
if (NOT DEFINED VALID_MACOS_SDK_VERSIONS)
# MacOS system supports the latest iteration of the new Accelerate
# BLAS/LAPACK. Use the default SDK, if it's recent enough.
if (MAIN_MACOS_SDK_VERSION VERSION_LESS MINIMUM_MACOS_SDK_VERSION)
message(STATUS "The MacOS SDK is: ${CMAKE_OSX_SYSROOT}")
message(STATUS "The minimum compatible MacOS SDK version is: ${MINIMUM_MACOS_SDK_VERSION}")
message(FATAL_ERROR "Please install a more recent XCode for a compatible MacOS SDK.")
endif ()
elseif (NOT MAIN_MACOS_SDK_VERSION IN_LIST VALID_MACOS_SDK_VERSIONS)
# MacOS system supports one of the past iterations of the new Accelerate
# BLAS/LAPACK. If the latest possible XCode is installed on this older
# system, there might be a mismatch between the SDK and the system binary.
# Find a matching SDK from the Command Line Tools instead (which must be
# manually installed).
foreach (_MACOS_SDK_VERSION IN LISTS VALID_MACOS_SDK_VERSIONS)
find_path(VALID_MACOS_SDK NAMES "MacOSX${_MACOS_SDK_VERSION}.sdk" HINTS "/Library/Developer/CommandLineTools/SDKs" NO_CACHE)
if (VALID_MACOS_SDK)
set(VALID_MACOS_SDK "${VALID_MACOS_SDK}/MacOSX${_MACOS_SDK_VERSION}.sdk")
break ()
endif ()
endforeach ()
if (VALID_MACOS_SDK)
# Override CMAKE_OSX_SYSROOT with a local variable.
set(CMAKE_OSX_SYSROOT "${VALID_MACOS_SDK}")
else ()
message(STATUS "The MacOS SDK is: ${CMAKE_OSX_SYSROOT}")
message(STATUS "The compatible MacOS SDK versions are: ${VALID_MACOS_SDK_VERSIONS}")
message(FATAL_ERROR "Couldn't find a compatible MacOS SDK. Please install compatible XCode Command Line Tools.")
endif ()
endif ()
message(STATUS "The MacOS SDK is: ${CMAKE_OSX_SYSROOT}")
FetchContent_Declare(
AccelerateLAPACK
GIT_REPOSITORY "https://github.com/lepus2589/accelerate-lapack.git"
GIT_TAG v1.4.0
SYSTEM
FIND_PACKAGE_ARGS 1.4.0 CONFIG NAMES AccelerateLAPACK
)
set(ENV{CMAKE_FRAMEWORK_PATH} "${CMAKE_OSX_SYSROOT}/System/Library/Frameworks")
set(AccelerateLAPACK_INCLUDE_PACKAGING FALSE)
set(BLA_VENDOR "Apple")
find_package(LAPACK MODULE)
unset(ENV{CMAKE_FRAMEWORK_PATH})
if (NOT LAPACK_FOUND OR NOT LAPACK_Accelerate_LIBRARY)
message(FATAL_ERROR "Couldn't find Accelerate framework in MacOS SDK!")
endif ()
FetchContent_MakeAvailable(AccelerateLAPACK)
add_subdirectory(src)
......@@ -25,119 +25,24 @@ SOFTWARE.
# Accelerate LAPACKE #
Since MacOS 13.3 Ventura, Apple's Accelerate framework comes with a new
[BLAS/LAPACK interface][accelerate-docs] compatible with [Reference LAPACK
v3.9.1][lapack-v3.9.1] in addition to the quite outdated [Reference LAPACK
v3.2.1][lapack-v3.2.1]. It also provides an ILP64 interface. On Apple Silicon
M-processors, it utilises the [proprietary AMX co-processor][apple-amx], 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][lapack-v3.11.0].
Unfortunately, there is no mention of it in the [MacOS 15.0 Sequoia Release
Notes][macos15-release-notes], but the note in the [Accelerate BLAS
docs][accelerate-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.
Unfortunately, Apple's Accelerate framework doesn't provide the LAPACKE
C-interface library. Since MacOS 13.3 Ventura, the [BLAS/LAPACK
interface][accelerate-docs] provided by Accelerate is recent enough to support
the LAPACKE library.
This project uses [Accelerate LAPACK][accelerate-lapack] to build the LAPACKE
library against the Accelerate NEWLAPACK interface. More info on BLAS/LAPACK in
Accelerate can be found there, too.
[accelerate-docs]: https://developer.apple.com/documentation/accelerate/blas-library
[apple-amx]: https://github.com/corsix/amx
[lapack-v3.2.1]: https://netlib.org/lapack/#_lapack_version_3_2_1
[lapack-v3.9.1]: https://github.com/Reference-LAPACK/lapack/releases/tag/v3.9.1
[lapack-v3.11.0]: https://github.com/Reference-LAPACK/lapack/releases/tag/v3.11.0
[macos15-release-notes]: https://developer.apple.com/documentation/macos-release-notes/macos-15-release-notes
- [The Problem](#the-problem)
- [The Solution](#the-solution)
- [The alias files (to use in other projects)](#the-alias-files-to-use-in-other-projects)
[accelerate-lapack]: https://github.com/lepus2589/accelerate-lapack
- [How to compile](#how-to-compile)
- [Prerequisites](#prerequisites)
- [Workflow with CMake](#workflow-with-cmake)
- [CMake v4 compatibility](#cmake-v4-compatibility)
- [Using LAPACKE in another project](#using-lapacke-in-another-project)
## The Problem ##
But what if you have to or just want to link against the Accelerate framework
without including the C/C++ headers, e.&nbsp;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][dgeqrt-ref] 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):
```plaintext
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?
[dgeqrt-ref]: https://github.com/Reference-LAPACK/lapack/blob/v3.11.0/CMakeLists.txt#L365-L366
## 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 additional linker options get attached to new interface library targets,
which are injected into the Reference LAPACK configure process using the
`CMAKE_REQUIRED_LIBRARIES` variable for the Fortran test compiles and the
`LINK_LIBRARIES` target property for the shared LAPACKE library.
This enables the compilation of the LAPACKE C-interface library for the
Accelerate framework (e.&nbsp;g. to be used in the `Eigen3` library). Analyzing
the resulting `.dylib` with `otool`, you can see:
```shell
$ 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.11.0)
/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 1351.0.0)
```
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`:
```plaintext
./build/<32|64>/src
├── new-blas-ilp64.alias
├── new-blas.alias
├── 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
......@@ -181,6 +86,19 @@ $ cmake --workflow --preset user-accelerate-lapacke32
I wouldn't recommend installing to `/usr/local` (used by Homebrew on Intel Macs)
or `/opt/local` (used by MacPorts).
Analyzing the resulting `.dylib` with `otool`, you can see:
```shell
$ 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.11.0)
/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 1351.0.0)
```
Only the Accelerate framework and the System library are linked into the
`.dylib`. No `libgfortran` or other libraries are needed.
#### CMake v4 compatibility ####
To build the project with CMake v4 or higher, you must explicitly provide the
......@@ -198,6 +116,12 @@ the CMake package in the other project's `CMakeLists.txt` file:
find_package(LAPACKE CONFIG)
```
or
```cmake
find_package(LAPACKE64 CONFIG)
```
and providing the above install location via the `CMAKE_PREFIX_PATH` variable
from the command line:
......@@ -205,5 +129,5 @@ 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`.
This makes the imported `lapacke`/`lapacke64` shared library target available in
the other project's `CMakeLists.txt`.
......@@ -23,114 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
include("../cmake/ParseTBDMacros.cmake")
# Paths to text-based .dylib stubs
set(BLAS_TBD_PATH "${LAPACK_Accelerate_LIBRARY}/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.tbd")
set(LAPACK_TBD_PATH "${LAPACK_Accelerate_LIBRARY}/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.tbd")
parse_tbd_symbols(BLAS "${BLAS_TBD_PATH}")
filter_new_lapack_symbols(BLAS)
filter_new_lapack_ilp64_symbols(BLAS)
parse_tbd_symbols(LAPACK "${LAPACK_TBD_PATH}")
filter_new_lapack_symbols(LAPACK)
filter_new_lapack_ilp64_symbols(LAPACK)
build_aliases(BLAS_NEWLAPACK_SYMBOLS)
build_aliases(BLAS_NEWLAPACK_ILP64_SYMBOLS)
build_aliases(LAPACK_NEWLAPACK_SYMBOLS)
build_aliases(LAPACK_NEWLAPACK_ILP64_SYMBOLS)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/new-blas.alias.in"
"${CMAKE_CURRENT_BINARY_DIR}/new-blas.alias"
@ONLY
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/new-lapack.alias.in"
"${CMAKE_CURRENT_BINARY_DIR}/new-lapack.alias"
@ONLY
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/new-blas-ilp64.alias.in"
"${CMAKE_CURRENT_BINARY_DIR}/new-blas-ilp64.alias"
@ONLY
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/new-lapack-ilp64.alias.in"
"${CMAKE_CURRENT_BINARY_DIR}/new-lapack-ilp64.alias"
@ONLY
)
add_library(NEW_BLAS INTERFACE IMPORTED GLOBAL)
target_link_libraries(NEW_BLAS INTERFACE ${BLAS_LIBRARIES})
add_library(NEW_LAPACK INTERFACE IMPORTED GLOBAL)
target_link_libraries(NEW_LAPACK INTERFACE ${LAPACK_LIBRARIES} NEW_BLAS)
# Add the $NEWLAPACK symbols to the linker flags of the NEW_BLAS and NEW_LAPACK
# targets
target_link_options(
NEW_BLAS
INTERFACE
${BLAS_LINKER_FLAGS}
"LINKER:-alias_list,${CMAKE_CURRENT_BINARY_DIR}/new-blas.alias"
)
target_link_options(
NEW_LAPACK
INTERFACE
${LAPACK_LINKER_FLAGS}
"LINKER:-alias_list,${CMAKE_CURRENT_BINARY_DIR}/new-lapack.alias"
)
add_library(NEW_BLAS64 INTERFACE IMPORTED GLOBAL)
target_link_libraries(NEW_BLAS64 INTERFACE ${BLAS_LIBRARIES})
add_library(NEW_LAPACK64 INTERFACE IMPORTED GLOBAL)
target_link_libraries(NEW_LAPACK64 INTERFACE ${LAPACK_LIBRARIES} NEW_BLAS64)
# Add the $NEWLAPACK$ILP64 symbols to the linker flags of the NEW_BLAS64 and
# NEW_LAPACK64 targets
target_link_options(
NEW_BLAS64
INTERFACE
${BLAS_LINKER_FLAGS}
"LINKER:-alias_list,${CMAKE_CURRENT_BINARY_DIR}/new-blas-ilp64.alias"
)
target_link_options(
NEW_LAPACK64
INTERFACE
${LAPACK_LINKER_FLAGS}
"LINKER:-alias_list,${CMAKE_CURRENT_BINARY_DIR}/new-lapack-ilp64.alias"
)
if (BUILD_INDEX64)
# Add the $NEWLAPACK$ILP64 symbols to the BLAS and LAPACK libraries.
set(BLAS_LIBRARIES "NEW_BLAS64")
set(LAPACK_LIBRARIES "NEW_LAPACK64")
set(CHECK_LAPACK_VERSION_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/lapack64_version.c")
else ()
# Add the $NEWLAPACK symbols to the linker flags of the lapacke target
set(BLAS_LIBRARIES "NEW_BLAS")
set(LAPACK_LIBRARIES "NEW_LAPACK")
set(CHECK_LAPACK_VERSION_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/lapack_version.c")
endif ()
set_target_properties(BLAS::BLAS PROPERTIES INTERFACE_LINK_LIBRARIES "${BLAS_LIBRARIES}")
set_target_properties(LAPACK::LAPACK PROPERTIES INTERFACE_LINK_LIBRARIES "${LAPACK_LIBRARIES}")
try_run(
HAS_RUN_ILAVER HAS_ILAVER
SOURCES ${CHECK_LAPACK_VERSION_SOURCE}
LINK_LIBRARIES ${LAPACK_LIBRARIES}
RUN_OUTPUT_VARIABLE ACCELERATE_LAPACK_ILAVER_VERSION
)
message(STATUS "The BLAS/LAPACK version provided by the Accelerate framework is confirmed to be: ${ACCELERATE_LAPACK_ILAVER_VERSION}")
get_target_property(ACCELERATE_NEW_LAPACK_VERSION LAPACK::NEW_LAPACK VERSION)
FetchContent_Declare(
reference-lapack
......@@ -150,14 +43,14 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL 4)
set(ENV{CMAKE_POLICY_VERSION_MINIMUM} 3.5)
endif ()
set(BLAS_LIBRARIES_BACKUP "${BLAS_LIBRARIES}")
set(LAPACK_LIBRARIES_BACKUP "${LAPACK_LIBRARIES}")
set(BLAS_LIBRARIES_BACKUP "BLAS::BLAS")
set(LAPACK_LIBRARIES_BACKUP "LAPACK::LAPACK")
# Set the BLAS and LAPACK libraries to the new 32bit targets. This helps the
# Reference LAPACK CMake script to pass the CheckFortranFunctionExists tests for
# `dgemm` and `dgeqrt`.
set(BLAS_LIBRARIES "NEW_BLAS")
set(LAPACK_LIBRARIES "NEW_LAPACK")
set(BLAS_LIBRARIES "BLAS::NEW_BLAS_IMPORTED")
set(LAPACK_LIBRARIES "LAPACK::NEW_LAPACK_IMPORTED")
FetchContent_MakeAvailable(reference-lapack)
......@@ -171,8 +64,14 @@ endif ()
if (BUILD_INDEX64)
# Replace the LINK_LIBRARIES of the lapacke64 target with the ILP64 ones.
# The lapacke64 target was created linking to the NEW_LAPACK target, which
# we had to add to satisfy the Check... tests, that the Reference LAPACK
# build script does.
# The lapacke64 target was created linking to the NEW_LAPACK_IMPORTED
# target, which we had to add to satisfy the Check... tests, that the
# Reference LAPACK build script does.
set_target_properties(lapacke64 PROPERTIES LINK_LIBRARIES "${LAPACK_LIBRARIES}")
else ()
# Replace the LINK_LIBRARIES of the lapacke target with the NEWLAPACK ones.
# The lapacke target was created linking to the NEW_LAPACK_IMPORTED target,
# which we had to add to satisfy the Check... tests, that the Reference
# LAPACK build script does.
set_target_properties(lapacke PROPERTIES LINK_LIBRARIES "${LAPACK_LIBRARIES}")
endif ()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment