Skip to content

Commit

Permalink
CMake: Use modern CMake to handle CUDA
Browse files Browse the repository at this point in the history
Replace the custom CUDA version detection code by CheckLanguage.
Replace the custom CMake option ESPRESSO_WITH_CUDA_COMPILER by
the CUDACXX environment variable.
  • Loading branch information
jngrad committed Dec 26, 2022
1 parent 2127076 commit f520206
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 116 deletions.
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Checks: |
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-macro-parentheses,
-bugprone-reserved-identifier,
clang-analyzer-alpha.*,
modernize-deprecated-headers,
modernize-make-shared,
Expand Down
4 changes: 2 additions & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ clang-sanitizer:
variables:
CC: 'clang-14'
CXX: 'clang++-14'
CUDAXX: 'clang++-14'
myconfig: 'maxset'
with_cuda: 'true'
with_cuda_compiler: 'clang'
with_coverage: 'false'
with_static_analysis: 'true'
check_skip_long: 'true'
Expand Down Expand Up @@ -427,9 +427,9 @@ empty:
variables:
CC: 'clang-14'
CXX: 'clang++-14'
CUDAXX: 'clang++-14'
myconfig: 'empty'
with_cuda: 'true'
with_cuda_compiler: 'clang'
with_static_analysis: 'true'
with_scafacos: 'false'
with_stokesian_dynamics: 'false'
Expand Down
44 changes: 38 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -177,25 +177,57 @@ endforeach()

# CUDA compiler
if(ESPRESSO_BUILD_WITH_CUDA)
set(ESPRESSO_DEFINE_CUDA_ARCHITECTURES OFF)
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
set(ESPRESSO_DEFINE_CUDA_ARCHITECTURES ON)
endif()
include(CheckLanguage)
enable_language(CUDA)
check_language(CUDA)
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD})
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
set(ESPRESSO_MINIMAL_CUDA_VERSION 11.0)
find_package(CUDAToolkit ${ESPRESSO_MINIMAL_CUDA_VERSION} REQUIRED)
espresso_option_enum(
varname "ESPRESSO_CUDA_COMPILER" help_text "CUDA compiler" default_value
"nvcc" possible_values "nvcc;clang")
if(ESPRESSO_CUDA_COMPILER STREQUAL "nvcc")
if(ESPRESSO_DEFINE_CUDA_ARCHITECTURES)
unset(ESPRESSO_CUDA_ARCHITECTURES)
# 1. sm_75: RTX-2000 series (Turing)
# 2. sm_61: GTX-1000 series (Pascal)
# 3. sm_52: GTX-900 series (Maxwell)
if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
list(APPEND ESPRESSO_CUDA_ARCHITECTURES 75)
list(APPEND ESPRESSO_CUDA_ARCHITECTURES 61)
elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")
# GTX-900 series (Maxwell)
if(CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 12)
list(APPEND ESPRESSO_CUDA_ARCHITECTURES 52)
endif()
if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10)
list(APPEND ESPRESSO_CUDA_ARCHITECTURES 61)
# With Clang 14+, architectures sm_70+ are only supported with Thrust
# 1.11+ from CUDA 11.3+, for details see
# https://github.com/NVIDIA/cub/pull/170
if((CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 14)
OR (CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.3.0))
list(APPEND ESPRESSO_CUDA_ARCHITECTURES 75)
endif()
endif()
endif()
# only override CMAKE_CUDA_ARCHITECTURES when dependencies are satisfied
if(DEFINED ESPRESSO_CUDA_ARCHITECTURES)
set(CMAKE_CUDA_ARCHITECTURES ${ESPRESSO_CUDA_ARCHITECTURES})
endif()
endif()
if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
find_package(CUDACompilerNVCC ${ESPRESSO_MINIMAL_CUDA_VERSION} REQUIRED)
elseif(ESPRESSO_CUDA_COMPILER STREQUAL "clang")
elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")
if(ESPRESSO_BUILD_WITH_COVERAGE)
message(
FATAL_ERROR
"Cannot enable code coverage with Clang as the CUDA compiler")
endif()
find_package(CUDACompilerClang 9.0 REQUIRED)
else()
message(FATAL_ERROR "Unknown CUDA compiler '${ESPRESSO_CUDA_COMPILER}'")
message(FATAL_ERROR "Unknown CUDA compiler '${CMAKE_CUDA_COMPILER_ID}'")
endif()
endif()

Expand Down
105 changes: 51 additions & 54 deletions cmake/FindCUDACompilerClang.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,74 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

# Find the Clang compiler, include its libraries and declare a custom
# `add_library()` wrapper function named `add_gpu_library()`.
# Verify the Clang compiler matches the NVIDIA toolkit,
# include the toolkit libraries and declare a custom
# `add_library()` wrapper function named `espresso_add_gpu_library()`.

if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL CMAKE_CUDA_COMPILER_ID)
message(
FATAL_ERROR
"To compile CUDA code with Clang, the C++ compiler must be Clang, not ${CMAKE_CXX_COMPILER_ID}."
"To compile CUDA code with ${CMAKE_CUDA_COMPILER_ID}, the C++ compiler must be ${CMAKE_CUDA_COMPILER_ID}, not ${CMAKE_CXX_COMPILER_ID}."
)
endif()

add_library(espresso_cuda_flags INTERFACE)
add_library(espresso::cuda_flags ALIAS espresso_cuda_flags)

function(detect_clang_cuda_path)
execute_process(COMMAND ${CMAKE_CUDA_COMPILER} ${CMAKE_CXX_FLAGS} --verbose
ERROR_VARIABLE CLANG_VERBOSE_OUTPUT)
if(CLANG_VERBOSE_OUTPUT MATCHES "Found CUDA installation")
set(CLANG_VERBOSE_OUTPUT ${CLANG_VERBOSE_OUTPUT} PARENT_SCOPE)
return()
endif()
if(NOT CMAKE_CXX_FLAGS MATCHES "--cuda-path")
foreach(unix_cuda_path /usr/lib/cuda /usr/local/cuda)
if(EXISTS ${unix_cuda_path})
execute_process(COMMAND ${CMAKE_CUDA_COMPILER} ${CMAKE_CXX_FLAGS}
"--cuda-path=${unix_cuda_path}" --verbose
ERROR_VARIABLE CLANG_VERBOSE_OUTPUT)
if(CLANG_VERBOSE_OUTPUT MATCHES "Found CUDA installation")
set(CLANG_VERBOSE_OUTPUT ${CLANG_VERBOSE_OUTPUT} PARENT_SCOPE)
message(STATUS "Clang did not automatically detect a compatible CUDA library; adding compiler flag --cuda-path=${unix_cuda_path}")
target_compile_options(espresso_cuda_flags INTERFACE "--cuda-path=${unix_cuda_path}")
return()
function(espresso_detect_clang_cuda_path)
separate_arguments(ESPRESSO_CMAKE_CUDA_FLAGS_LIST NATIVE_COMMAND "${CMAKE_CUDA_FLAGS}")
execute_process(COMMAND ${CMAKE_CUDA_COMPILER} ${ESPRESSO_CMAKE_CUDA_FLAGS_LIST} ${ARGV} --verbose
ERROR_VARIABLE ESPRESSO_CLANG_VERBOSE_OUTPUT)
set(ESPRESSO_CLANG_VERBOSE_OUTPUT ${ESPRESSO_CLANG_VERBOSE_OUTPUT} PARENT_SCOPE)
endfunction()

espresso_detect_clang_cuda_path()
if(NOT ESPRESSO_CLANG_VERBOSE_OUTPUT MATCHES "Found CUDA installation")
unset(ESPRESSO_CLANG_HINT_CUDA_PATHS)
if(NOT CMAKE_CUDA_FLAGS MATCHES "--cuda-path")
foreach(ESPRESSO_UNIX_CUDA_PATH ${CUDAToolkit_ROOT} /usr/lib/cuda /usr/local/cuda)
if(EXISTS ${ESPRESSO_UNIX_CUDA_PATH})
espresso_detect_clang_cuda_path("--cuda-path=${ESPRESSO_UNIX_CUDA_PATH}")
if(ESPRESSO_CLANG_VERBOSE_OUTPUT MATCHES "Found CUDA installation")
list(APPEND ESPRESSO_CLANG_HINT_CUDA_PATHS "${ESPRESSO_UNIX_CUDA_PATH}")
endif()
endif()
endforeach()
endif()
endfunction()

set(CMAKE_CUDA_COMPILER ${CMAKE_CXX_COMPILER})
set(CMAKE_CUDA_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION})
set(ESPRESSO_CLANG_HINT_CUDA_PATHS_STR "")
if(DEFINED ESPRESSO_CLANG_HINT_CUDA_PATHS)
list(JOIN ESPRESSO_CLANG_HINT_CUDA_PATHS " " ESPRESSO_CLANG_HINT_CUDA_PATHS_STR)
set(ESPRESSO_CLANG_HINT_CUDA_PATHS_STR " (possible paths: ${ESPRESSO_CLANG_HINT_CUDA_PATHS_STR})")
endif()
message(FATAL_ERROR "${CMAKE_CUDA_COMPILER_ID} could not automatically detect a compatible CUDA library; try hinting one with both '-D CUDAToolkit_ROOT=\"...\"' and '-D CMAKE_CUDA_FLAGS=\"--cuda-path=...\"'${ESPRESSO_CLANG_HINT_CUDA_PATHS_STR}.")
endif()

detect_clang_cuda_path()
string(REGEX REPLACE "^.*Found CUDA installation: ([^,]+).*\$" "\\1" CUDA_DIR
"${CLANG_VERBOSE_OUTPUT}")
string(REGEX REPLACE "^.*Found CUDA installation: ([^,]+).*\$" "\\1"
ESPRESSO_CLANG_DETECTED_CUDA_DIR "${ESPRESSO_CLANG_VERBOSE_OUTPUT}")
string(REGEX REPLACE "^.*Found CUDA installation: .* version ([0-9\.]+|unknown).*\$"
"\\1" CUDA_VERSION "${CLANG_VERBOSE_OUTPUT}")
message(STATUS "Found CUDA-capable host compiler: ${CMAKE_CUDA_COMPILER}")
if(NOT CLANG_VERBOSE_OUTPUT MATCHES "Found CUDA installation" OR CUDA_VERSION STREQUAL "unknown")
message(STATUS "Clang did not automatically detect a compatible CUDA library; adding compiler flag -Wno-unknown-cuda-version")
target_compile_options(espresso_cuda_flags INTERFACE -Wno-unknown-cuda-version)
message(STATUS "Found CUDA version: ${CUDAToolkit_VERSION}")
message(STATUS "Found CUDA installation: ${CUDAToolkit_LIBRARY_DIR}")
else()
if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "12.0.0" AND
CMAKE_CUDA_COMPILER_VERSION VERSION_LESS "13.0.0" AND
CUDA_VERSION VERSION_GREATER_EQUAL "11.0" AND
CUDA_VERSION VERSION_LESS "12.0")
message(STATUS "Clang ${CMAKE_CXX_COMPILER_VERSION} doesn't natively support CUDA ${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}; adding compiler flag -Wno-unknown-cuda-version")
"\\1" ESPRESSO_CLANG_DETECTED_CUDA_VERSION "${ESPRESSO_CLANG_VERBOSE_OUTPUT}")

if(NOT "${ESPRESSO_CLANG_DETECTED_CUDA_DIR}" STREQUAL "${CUDAToolkit_ROOT}")
set(ESPRESSO_CUDA_TOOLKIT_MISMATCH_WARN "${CMAKE_CUDA_COMPILER_ID} CUDA toolkit directory (${ESPRESSO_CLANG_DETECTED_CUDA_DIR}) and NVIDIA CUDA toolkit directory (${CUDAToolkit_ROOT}) don't match")
if("${CUDAToolkit_ROOT}" STREQUAL "")
message(WARNING "${ESPRESSO_CUDA_TOOLKIT_MISMATCH_WARN}; try hinting it with '-D CUDAToolkit_ROOT=\"${ESPRESSO_CLANG_DETECTED_CUDA_DIR}\"'.")
else()
message(WARNING "${ESPRESSO_CUDA_TOOLKIT_MISMATCH_WARN}; try hinting it with '-D CMAKE_CUDA_FLAGS=\"--cuda-path=${CUDAToolkit_ROOT}\"'.")
endif()
endif()

if(NOT CMAKE_CUDA_FLAGS MATCHES "-Wno-unknown-cuda-version")
set(ESPRESSO_CUDA_TOOLKIT_UNKNOWN_WARN "use '-D CMAKE_CUDA_FLAGS=\"-Wno-unknown-cuda-version\"' to override this check")
if(ESPRESSO_CLANG_DETECTED_CUDA_VERSION STREQUAL "unknown")
message(FATAL_ERROR "${CMAKE_CUDA_COMPILER_ID} could not detect the version of the CUDA toolkit library; ${ESPRESSO_CUDA_TOOLKIT_UNKNOWN_WARN}")
elseif(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "12.0.0" AND
CMAKE_CUDA_COMPILER_VERSION VERSION_LESS "13.0.0" AND
ESPRESSO_CLANG_DETECTED_CUDA_VERSION VERSION_LESS "12.0")
message(WARNING "${CMAKE_CUDA_COMPILER_ID} ${CMAKE_CUDA_COMPILER_VERSION} doesn't natively support CUDA ${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}; ${ESPRESSO_CUDA_TOOLKIT_UNKNOWN_WARN}.")
target_compile_options(espresso_cuda_flags INTERFACE -Wno-unknown-cuda-version)
endif()
message(STATUS "Found CUDA version: ${CUDAToolkit_VERSION} (recognized by Clang as ${CUDA_VERSION})")
message(STATUS "Found CUDA installation: ${CUDA_DIR}")
endif()
set(CUDA_VERSION ${CUDAToolkit_VERSION})
message(STATUS "Found CUDA toolkit installation: ${ESPRESSO_CLANG_DETECTED_CUDA_DIR} (recognized by ${CMAKE_CUDA_COMPILER_ID} as CUDA ${ESPRESSO_CLANG_DETECTED_CUDA_VERSION})")

target_compile_options(
espresso_cuda_flags
Expand All @@ -92,14 +97,6 @@ target_compile_options(
$<$<CONFIG:RelWithDebInfo>:-O2 -g -DNDEBUG>
$<$<CONFIG:Coverage>:-O3 -g>
$<$<CONFIG:RelWithAssert>:-O3 -g>
# GTX-900 series (Maxwell)
$<$<VERSION_LESS:${CMAKE_CUDA_COMPILER_VERSION},12>:--cuda-gpu-arch=sm_52>
# GTX-1000 series (Pascal)
$<$<VERSION_GREATER_EQUAL:${CMAKE_CUDA_COMPILER_VERSION},10>:--cuda-gpu-arch=sm_61>
# RTX-2000 series (Turing)
# With Clang 14+, architectures sm_70+ are only supported with Thrust 1.11+
# from CUDA 11.3+, for details see https://github.com/NVIDIA/cub/pull/170
$<$<AND:$<VERSION_GREATER_EQUAL:${CMAKE_CUDA_COMPILER_VERSION},10>,$<OR:$<VERSION_LESS:${CMAKE_CUDA_COMPILER_VERSION},14>,$<VERSION_GREATER_EQUAL:${CUDA_VERSION},11.3.0>>>:--cuda-gpu-arch=sm_75>
)

function(espresso_add_gpu_library)
Expand All @@ -110,7 +107,7 @@ function(espresso_add_gpu_library)
list(GET ARG_UNPARSED_ARGUMENTS 0 TARGET_NAME)
list(REMOVE_AT ARG_UNPARSED_ARGUMENTS 0)
set(TARGET_SOURCES ${ARG_UNPARSED_ARGUMENTS})
set_source_files_properties(${TARGET_SOURCES} PROPERTIES LANGUAGE "CXX")
set_source_files_properties(${TARGET_SOURCES} PROPERTIES LANGUAGE "CUDA")
add_library(${ARGV})
set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE "CXX")
target_link_libraries(${TARGET_NAME} PRIVATE espresso::cuda_flags)
Expand Down
34 changes: 9 additions & 25 deletions cmake/FindCUDACompilerNVCC.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,23 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

# Find the NVCC compiler, include its libraries and declare a custom
# `add_library()` wrapper function named `add_gpu_library()`.
# Verify the NVCC compiler matches the NVIDIA toolkit,
# include the toolkit libraries and declare a custom
# `add_library()` wrapper function named `espresso_add_gpu_library()`.

set(CMAKE_CUDA_COMPILER ${CUDAToolkit_NVCC_EXECUTABLE})

execute_process(COMMAND ${CMAKE_CUDA_COMPILER} --version
OUTPUT_VARIABLE NVCC_VERSION_STRING)

string(REGEX
REPLACE "^.*Cuda compilation tools, release [0-9\.]+, V([0-9\.]+).*\$"
"\\1" CMAKE_CUDA_COMPILER_VERSION "${NVCC_VERSION_STRING}")

get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT "${CUDA_TOOLKIT_ROOT_DIR}/bin/nvcc" REALPATH)
get_filename_component(CMAKE_CUDA_COMPILER_RESOLVED "${CMAKE_CUDA_COMPILER}" REALPATH)
if(NOT "${CMAKE_CUDA_COMPILER_TOOLKIT}" STREQUAL "${CMAKE_CUDA_COMPILER_RESOLVED}"
get_filename_component(ESPRESO_CUDAToolkit_ROOT_RESOLVED "${CUDAToolkit_ROOT}/bin/nvcc" REALPATH)
get_filename_component(ESPRESO_CMAKE_CUDA_COMPILER_RESOLVED "${CMAKE_CUDA_COMPILER}" REALPATH)
if(NOT "${ESPRESO_CUDAToolkit_ROOT_RESOLVED}" STREQUAL "${ESPRESO_CMAKE_CUDA_COMPILER_RESOLVED}"
AND NOT ESPRESSO_INSIDE_DOCKER)
get_filename_component(NVCC_EXECUTABLE_DIRNAME "${CMAKE_CUDA_COMPILER}" DIRECTORY)
get_filename_component(NVCC_EXECUTABLE_DIRNAME "${NVCC_EXECUTABLE_DIRNAME}" DIRECTORY)
get_filename_component(ESPRESSO_NVCC_EXECUTABLE_DIRNAME "${CMAKE_CUDA_COMPILER}" DIRECTORY)
get_filename_component(ESPRESSO_NVCC_EXECUTABLE_DIRNAME "${ESPRESSO_NVCC_EXECUTABLE_DIRNAME}" DIRECTORY)
message(
WARNING
"Your nvcc (${CMAKE_CUDA_COMPILER}) does not appear to match your CUDA libraries (in ${CUDA_TOOLKIT_ROOT_DIR}). While ESPResSo will still compile, you might get unexpected crashes. Please point CUDA_TOOLKIT_ROOT_DIR to your CUDA toolkit path, e.g. by adding -DCUDA_TOOLKIT_ROOT_DIR='${NVCC_EXECUTABLE_DIRNAME}' to your cmake command."
"Your nvcc compiler (${CMAKE_CUDA_COMPILER}) does not appear to match your CUDA toolkit installation (${CUDAToolkit_ROOT}). While ESPResSo will still compile, you might get unexpected crashes. Try hinting it with '-D CUDAToolkit_ROOT=\"${ESPRESSO_NVCC_EXECUTABLE_DIRNAME}\"'."
)
endif()

set(CUDA_LINK_LIBRARIES_KEYWORD PUBLIC)

set(CUDA_PROPAGATE_HOST_FLAGS OFF)

add_library(espresso_cuda_flags INTERFACE)
Expand All @@ -58,7 +49,6 @@ target_compile_options(
$<$<CONFIG:RelWithDebInfo>:-Xptxas=-O2 -Xcompiler=-O2,-g -DNDEBUG>
$<$<CONFIG:Coverage>:-Xptxas=-O3 -Xcompiler=-Og,-g>
$<$<CONFIG:RelWithAssert>:-Xptxas=-O3 -Xcompiler=-O3,-g>
"--compiler-bindir=${CMAKE_CXX_COMPILER}"
$<$<BOOL:${ESPRESSO_WARNINGS_ARE_ERRORS}>:-Xcompiler=-Werror;-Xptxas=-Werror>
$<$<BOOL:${CMAKE_OSX_SYSROOT}>:-Xcompiler=-isysroot;-Xcompiler=${CMAKE_OSX_SYSROOT}>
)
Expand All @@ -68,12 +58,6 @@ function(espresso_add_gpu_library)
set(TARGET_NAME ${ARGV0})
set_target_properties(${TARGET_NAME} PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_link_libraries(${TARGET_NAME} PRIVATE espresso::cuda_flags)
list(APPEND cuda_archs 75) # RTX-2000 series (Turing)
list(APPEND cuda_archs 61) # GTX-1000 series (Pascal)
if(CMAKE_CUDA_COMPILER_VERSION LESS 11)
list(APPEND cuda_archs 52) # GTX-900 series (Maxwell)
endif()
set_target_properties(${TARGET_NAME} PROPERTIES CUDA_ARCHITECTURES "${cuda_archs}")
endfunction()

include(FindPackageHandleStandardArgs)
Expand Down
Loading

0 comments on commit f520206

Please sign in to comment.