blob: 073f14e02ad84a7ac6ef847277b94a186a89929a [file] [edit]
# SPDX-FileCopyrightText: The Eigen Authors
# SPDX-License-Identifier: MPL-2.0
# How the .cu tests are compiled. Everything the GPU test build decides lives here, so ei_add_test_internal only
# has to ask which of three modes is in force:
#
# cuda-language CMake's first-class CUDA language, the default. nvcc, or clang as the CUDA compiler.
# cuda-as-cxx the .cu files compiled as C++ by a compiler that drives CUDA itself. nvc++ has no CMake CUDA
# language support, and CMake refuses clang as the CUDA compiler on Windows (issue #20776).
# hip CMake's HIP language.
#
# ei_gpu_testing_enable() is called once from the top-level CMakeLists, which is where enable_language() has to
# happen: it affects the calling directory and below, and test/ and contrib/test/ are siblings.
# Resolve EIGEN_CUDA_COMPUTE_ARCH against the toolkit version. Empty selects the oldest architecture the toolkit
# still compiles for: sm_60, Eigen's documented floor (see Macros.h), or sm_75 from CUDA 13 on, which dropped
# offline compilation for Pascal and Volta. Honor CMake's architecture setting (including CUDAARCHS) when the
# Eigen-specific override is empty.
macro(ei_cuda_resolve_compute_arch)
if("${EIGEN_CUDA_COMPUTE_ARCH}" STREQUAL "")
if(DEFINED CMAKE_CUDA_ARCHITECTURES AND NOT "${CMAKE_CUDA_ARCHITECTURES}" STREQUAL "")
set(EIGEN_CUDA_COMPUTE_ARCH "${CMAKE_CUDA_ARCHITECTURES}")
elseif(NOT "$ENV{CUDAARCHS}" STREQUAL "")
set(EIGEN_CUDA_COMPUTE_ARCH "$ENV{CUDAARCHS}")
elseif(CUDAToolkit_VERSION VERSION_LESS 13.0)
set(EIGEN_CUDA_COMPUTE_ARCH 60)
else()
set(EIGEN_CUDA_COMPUTE_ARCH 75)
endif()
endif()
set_property(GLOBAL PROPERTY EIGEN_CUDA_COMPUTE_ARCH_RESOLVED "${EIGEN_CUDA_COMPUTE_ARCH}")
endmacro()
macro(ei_gpu_testing_enable)
set(EIGEN_GPU_TEST_MODE "none")
if(EIGEN_TEST_CUDA)
# Honours CUDAToolkit_ROOT, which is how one of several installed toolkits is chosen. Without it, an explicit nvcc
# selects its own toolkit, <root>/bin/nvcc. find_package derives that only once the CUDA language is enabled, which
# needs the toolkit version first; a toolkit other than the compiler's links a second CUDA runtime.
if(CMAKE_CUDA_COMPILER AND NOT EIGEN_TEST_CUDA_NVC AND NOT EIGEN_TEST_CUDA_CLANG AND
NOT DEFINED CUDAToolkit_ROOT AND NOT DEFINED ENV{CUDAToolkit_ROOT})
get_filename_component(CUDAToolkit_ROOT "${CMAKE_CUDA_COMPILER}" PROGRAM)
get_filename_component(CUDAToolkit_ROOT "${CUDAToolkit_ROOT}" DIRECTORY)
get_filename_component(CUDAToolkit_ROOT "${CUDAToolkit_ROOT}" DIRECTORY)
endif()
find_package(CUDAToolkit 11.8 REQUIRED)
ei_cuda_resolve_compute_arch()
if(EIGEN_TEST_CUDA_NVC OR (EIGEN_TEST_CUDA_CLANG AND WIN32))
set(EIGEN_GPU_TEST_MODE "cuda-as-cxx")
# CMake's named and suffixed architectures cannot be used verbatim in these compiler flags.
foreach(arch IN LISTS EIGEN_CUDA_COMPUTE_ARCH)
if(NOT arch MATCHES "^[0-9]+$")
message(FATAL_ERROR "EIGEN_CUDA_COMPUTE_ARCH=${EIGEN_CUDA_COMPUTE_ARCH}: this compiler route takes numeric "
"architectures only, for example 75;89.")
endif()
endforeach()
if(EIGEN_TEST_CUDA_NVC)
string(APPEND CMAKE_CXX_FLAGS " -cuda")
foreach(arch IN LISTS EIGEN_CUDA_COMPUTE_ARCH)
string(APPEND CMAKE_CXX_FLAGS " -gpu=cc${arch}")
endforeach()
else()
string(APPEND CMAKE_CXX_FLAGS " --cuda-path=${CUDAToolkit_TARGET_DIR}")
foreach(arch IN LISTS EIGEN_CUDA_COMPUTE_ARCH)
string(APPEND CMAKE_CXX_FLAGS " --cuda-gpu-arch=sm_${arch}")
endforeach()
endif()
string(APPEND CMAKE_CXX_FLAGS " ${EIGEN_CUDA_CXX_FLAGS}")
else()
set(EIGEN_GPU_TEST_MODE "cuda-language")
# CMP0104 is NEW from 3.18, so the architectures have to be known before the language is enabled.
if(EIGEN_CUDA_COMPUTE_ARCH MATCHES "native|all|all-major" AND CMAKE_VERSION VERSION_LESS 3.24)
message(FATAL_ERROR "EIGEN_CUDA_COMPUTE_ARCH=${EIGEN_CUDA_COMPUTE_ARCH} needs CMake 3.24 or later; "
"name the architectures instead, for example 75;89.")
endif()
set(CMAKE_CUDA_ARCHITECTURES "${EIGEN_CUDA_COMPUTE_ARCH}")
if(NOT CMAKE_CUDA_COMPILER)
if(EIGEN_TEST_CUDA_CLANG)
set(CMAKE_CUDA_COMPILER "${CMAKE_CXX_COMPILER}")
else()
set(CMAKE_CUDA_COMPILER "${CUDAToolkit_NVCC_EXECUTABLE}")
endif()
endif()
# nvcc drives a host compiler; keep it the one the rest of the build uses. MSVC picks its own.
if(NOT EIGEN_TEST_CUDA_CLANG AND NOT MSVC AND NOT CMAKE_CUDA_HOST_COMPILER)
set(CMAKE_CUDA_HOST_COMPILER "${CMAKE_CXX_COMPILER}")
endif()
if(NOT CMAKE_CUDA_STANDARD AND CMAKE_CXX_STANDARD)
set(CMAKE_CUDA_STANDARD "${CMAKE_CXX_STANDARD}")
endif()
# ccache in front of the C++ compiler should front the CUDA compiler too, which is what the generated nvcc
# wrapper script used to arrange for FindCUDA.
if(NOT CMAKE_CUDA_COMPILER_LAUNCHER AND CMAKE_CXX_COMPILER_LAUNCHER)
set(CMAKE_CUDA_COMPILER_LAUNCHER "${CMAKE_CXX_COMPILER_LAUNCHER}")
endif()
# Compiler identification compiles with CMAKE_CUDA_FLAGS, so flags it depends on go in before the language is
# enabled: clang looks in /usr/local/cuda unless told otherwise, which need not be the toolkit found above, and
# refuses a version it does not support; custom flags may carry nvcc's -allow-unsupported-compiler.
set(eigen_cuda_flags "${EIGEN_CUDA_CXX_FLAGS}")
if(EIGEN_TEST_CUDA_CLANG)
string(PREPEND eigen_cuda_flags "--cuda-path=${CUDAToolkit_TARGET_DIR} ")
endif()
set(eigen_cuda_flags_initialized FALSE)
if(DEFINED CMAKE_CUDA_FLAGS)
set(eigen_cuda_flags_initialized TRUE)
else()
set(CMAKE_CUDA_FLAGS "$ENV{CUDAFLAGS} ${CMAKE_CUDA_FLAGS_INIT}")
endif()
string(APPEND CMAKE_CUDA_FLAGS " ${eigen_cuda_flags}")
enable_language(CUDA)
if(NOT eigen_cuda_flags_initialized)
# Reveal CMake's initialized cache, including platform defaults added after compiler identification.
# Keep Eigen's additions out of that cache so reconfiguration neither duplicates nor retains old flags.
unset(CMAKE_CUDA_FLAGS)
string(APPEND CMAKE_CUDA_FLAGS " ${eigen_cuda_flags}")
endif()
unset(eigen_cuda_flags)
unset(eigen_cuda_flags_initialized)
if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
string(APPEND CMAKE_CUDA_FLAGS " --expt-relaxed-constexpr -Xcudafe \"--display_error_number\"")
endif()
message(STATUS "CUDA tests: ${CMAKE_CUDA_COMPILER_ID} ${CMAKE_CUDA_COMPILER_VERSION}, "
"architectures ${CMAKE_CUDA_ARCHITECTURES}")
endif()
elseif(EIGEN_TEST_HIP)
if(CMAKE_VERSION VERSION_LESS 3.21)
message(FATAL_ERROR "EIGEN_TEST_HIP needs CMake 3.21 or later for the HIP language.")
endif()
set(EIGEN_GPU_TEST_MODE "hip")
# ROCM_PATH selected the installation for the find_package(HIP) path this replaces. The HIP language finds
# ROCm through CMAKE_HIP_COMPILER_ROCM_ROOT and the Clang shipped beside it, so seed both from ROCM_PATH,
# leaving anything the user configured directly, and anything CMake's own hipconfig probe finds when
# ROCM_PATH names no installation, untouched.
set(ROCM_PATH "/opt/rocm" CACHE PATH "Path to the ROCm installation.")
if(IS_DIRECTORY "${ROCM_PATH}")
if(NOT DEFINED CMAKE_HIP_COMPILER_ROCM_ROOT)
set(CMAKE_HIP_COMPILER_ROCM_ROOT "${ROCM_PATH}")
endif()
if(NOT DEFINED CMAKE_HIP_COMPILER AND EXISTS "${ROCM_PATH}/llvm/bin/clang++")
set(CMAKE_HIP_COMPILER "${ROCM_PATH}/llvm/bin/clang++")
endif()
endif()
# Compiler ABI detection needs architectures even on build-only hosts without an AMD GPU.
if(NOT DEFINED EIGEN_HIP_ARCHITECTURES)
set(EIGEN_HIP_ARCHITECTURES
gfx900;gfx906;gfx908;gfx90a;gfx940;gfx941;gfx942;gfx1030;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151
CACHE STRING "HIP GPU architectures to build Eigen's HIP tests for.")
endif()
# A nonempty false value such as OFF is CMake's request for no architecture flags, not an absent setting.
if("${CMAKE_HIP_ARCHITECTURES}" STREQUAL "")
set(CMAKE_HIP_ARCHITECTURES "${EIGEN_HIP_ARCHITECTURES}")
endif()
enable_language(HIP)
message(STATUS "HIP tests: ${CMAKE_HIP_COMPILER_ID} ${CMAKE_HIP_COMPILER_VERSION}, "
"architectures ${CMAKE_HIP_ARCHITECTURES}")
endif()
endmacro()
# The GPU part of ei_add_test_internal: build one .cu test in whichever mode is in force.
function(ei_add_gpu_test_executable targetname filename)
if(EIGEN_GPU_TEST_MODE STREQUAL "hip")
set_source_files_properties(${filename} PROPERTIES LANGUAGE HIP)
add_executable(${targetname} ${filename})
target_compile_definitions(${targetname} PRIVATE EIGEN_USE_HIP)
elseif(EIGEN_GPU_TEST_MODE STREQUAL "cuda-as-cxx")
# The compiler drives CUDA itself, from flags set in ei_gpu_testing_enable.
set_source_files_properties(${filename} PROPERTIES LANGUAGE CXX)
add_executable(${targetname} ${filename})
# nvc++ already links its shared runtime. Adding the static runtime duplicates kernel registration state.
if(NOT EIGEN_TEST_CUDA_NVC)
target_link_libraries(${targetname} CUDA::cudart_static)
endif()
else()
add_executable(${targetname} ${filename})
target_link_libraries(${targetname} CUDA::cudart_static)
endif()
endfunction()