I'm working in a general CMake script that provides a grateful fallback strategy regarding activation of vectorization capabilities while compiling a project with Eigen.
Using Eigen 3.4 with C 17 standard and compiled with an updated compiler (ex. gcc > 7), there is no code requirements for the developer to comply regarding static objects alignment. No more EIGEN_MAKE_ALIGNED_OPERATOR_NEW macro and explicit alignment in STL containers (and we cheered hard in our lab for this!).
But we have a diverse landscape of projects with diverse compilation setups. We would like to write our code without worrying about alignment while a CMake script detects the architecture compilation capabilities and deactivates Eigen's unsafe vectorization features for the architecture (even when this means breaking abi-compatibility).
Is there a way to retrieve the architecture max alignment size at CMake level, to properly set the EIGEN_MAX_STATIC_ALIGN_BYTES directive??
Maybe a bash script that retrieves the max_align_t? https://en.cppreference.com/w/cpp/types/max_align_t
CodePudding user response:
You can use the source code from https://en.cppreference.com/w/cpp/types/max_align_t with try_run
. Note that cross compilation requires additional considerations; refer to the documentation of try_run
for more on this.
cmake_tests/alignment_check.cpp:
This is the source from https://en.cppreference.com/w/cpp/types/max_align_t
#include <iostream>
#include <cstddef>
int main()
{
std::cout << alignof(std::max_align_t) << '\n';
}
CMakeLists.txt
...
try_run(MY_MAXALIGN_RUN_RESULT MY_MAXALIGN_COMPILE_SUCCESS
${CMAKE_CURRENT_BINARY_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/cmake_tests/alignment_check.cpp"
COMPILE_OUTPUT_VARIABLE MY_MAXALIGN_COMPILE_OUTPUT
RUN_OUTPUT_VARIABLE MY_MAXALIGN_RUN_OUTPUT)
if (NOT MY_MAXALIGN_COMPILE_SUCCESS)
message(FATAL_ERROR "Error during compilation of ${CMAKE_CURRENT_SOURCE_DIR}/cmake_tests/alignment_check.cpp :\n\n${MY_MAXALIGN_COMPILE_OUTPUT}")
endif()
if (MY_MAXALIGN_RUN_RESULT)
message(FATAL_ERROR "Error running logic in ${CMAKE_CURRENT_SOURCE_DIR}/cmake_tests/alignment_check.cpp :\n\n${MY_MAXALIGN_RUN_OUTPUT}")
endif()
string(STRIP ${MY_MAXALIGN_RUN_OUTPUT} MY_MAXALIGN)
message(STATUS "MY_MAXALIGN = \"${MY_MAXALIGN}\"")
...
The content of the MY_MAXALIGN
cmake variable can be used to e.g. specify a compile definition.
CMake output
...
MY_MAXALIGN = "8"
...
CodePudding user response:
While fabian idea is great it has one huge drawback - you have to run the executable. Not always can you run the executable. The goal is to get the value of alignof(std::max_align_t)
without running anything, only with compilation. And we can do it.
As always, learn from the best. From CMake modules inspect files: CMakeCompilerABI.h
CMakeCCompilerABI.c
CMakeDetermineCompilerABI.cmake
. Note how CMakeCompilerABI.h
embeds the information into executable, without actually printing it. Take the same approuch. Your source file would look like the following:
// determineAlingofMax.cpp
#include <cstddef>
#define VAL alignof(std::max_align_t)
const char info_alingof_max_align_t[] = {
'I', 'N', 'F', 'O', ':', 'a', 'l', 'i', 'n', 'g', 'o', 'f', 'm', 'a', 'x'
'[', '0' ((VAL / 10) % 10), '0' (VAL % 10), ']', '\0',
};
int main(int argc, char *argv[]) {
return info_alingof_max_align_t[argc];
}
Note how we calculated the alignof and converted it to a string at compile time.
Now we have to compile it to an object file - we do not need to run any executable. So we take a look at CMakeDetermineCompilerABI.cmake
and write similar:
set(BIN "${CMAKE_BINARY_DIR}/determineAlingofMax.bin"
try_compile(ALIGNMAX_COMPILED
${CMAKE_BINARY_DIR}
SOURCES /path/to/the/determineAlingofMax.cpp
CMAKE_FLAGS ${CMAKE_FLAGS}
# Ignore unused flags
"--no-warn-unused-cli"
COMPILE_DEFINITIONS ${COMPILE_DEFINITIONS}
COPY_FILE "${BIN}"
COPY_FILE_ERROR copy_error
OUTPUT_VARIABLE OUTPUT
)
if (ALIGNMAX_COMPILED AND not copy_error)
file(STRINGS "${BIN}" data REGEX "INFO:alingofmax\\[[^]]*\\]")
if (data MATCHES "INFO:alingofmax\\[0*([^]]*)\\]")
set(ALINGOFMAX "${CMAKE_MATCH_1}" CACHE INTERNAL "")
endif()
endif()
if (NOT ALINGOFMAX)
message(FATAL_ERROR some error here)
endif()
# and finally, maybe option()
set(EIGEN_MAX_STATIC_ALIGN_BYTES ${ALINGOFMAX})
Note how the binary is not executed - only compiled. The string INFO:alignofmax
has to be only unique enough, so it does not come anywhere in the executable - I prefer to use UUIDs lately.