1##===----------------------------------------------------------------------===## 2# 3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4# See https://llvm.org/LICENSE.txt for license information. 5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6# 7##===----------------------------------------------------------------------===## 8# 9# Find OpenMP Target offloading Support for various compilers. 10# 11##===----------------------------------------------------------------------===## 12 13#[========================================================================[.rst: 14FindOpenMPTarget 15---------------- 16 17Finds OpenMP Target Offloading Support. 18 19This module can be used to detect OpenMP target offloading support in a 20compiler. If the compiler support OpenMP Offloading to a specified target, the 21flags required to compile offloading code to that target are output for each 22target. 23 24This module will automatically include OpenMP support if it was not loaded 25already. It does not need to be included separately to get full OpenMP support. 26 27Variables 28^^^^^^^^^ 29 30The module exposes the components ``NVPTX`` and ``AMDGPU``. Each of these 31controls the various offloading targets to search OpenMP target offloasing 32support for. 33 34Depending on the enabled components the following variables will be set: 35 36``OpenMPTarget_FOUND`` 37 Variable indicating that OpenMP target offloading flags for all requested 38 targets have been found. 39 40This module will set the following variables per language in your 41project, where ``<device>`` is one of NVPTX or AMDGPU 42 43``OpenMPTarget_<device>_FOUND`` 44 Variable indicating if OpenMP support for the ``<device>`` was detected. 45``OpenMPTarget_<device>_FLAGS`` 46 OpenMP compiler flags for offloading to ``<device>``, separated by spaces. 47 48For linking with OpenMP code written in ``<device>``, the following 49variables are provided: 50 51``OpenMPTarget_<device>_LIBRARIES`` 52 A list of libraries needed to link with OpenMP code written in ``<lang>``. 53 54Additionally, the module provides :prop_tgt:`IMPORTED` targets: 55 56``OpenMPTarget::OpenMPTarget_<device>`` 57 Target for using OpenMP offloading to ``<device>``. 58 59If the specific architecture of the target is needed, it can be manually 60specified by setting a variable to the desired architecture. Variables can also 61be used to override the standard flag searching for a given compiler. 62 63``OpenMPTarget_<device>_ARCH`` 64 Sets the architecture of ``<device>`` to compile for. Such as `sm_70` for NVPTX 65 or `gfx908` for AMDGPU. 66 67``OpenMPTarget_<device>_DEVICE`` 68 Sets the name of the device to offload to. 69 70``OpenMPTarget_<device>_FLAGS`` 71 Sets the compiler flags for offloading to ``<device>``. 72 73#]========================================================================] 74 75# TODO: Support Fortran 76# TODO: Support multiple offloading targets by setting the "OpenMPTarget" target 77# to include flags for all components loaded 78# TODO: Configure target architecture without a variable (component NVPTX_SM_70) 79# TODO: Test more compilers 80 81cmake_policy(PUSH) 82cmake_policy(VERSION 3.20.0) 83 84find_package(OpenMP ${OpenMPTarget_FIND_VERSION} REQUIRED) 85 86# Find the offloading flags for each compiler. 87function(_OPENMP_TARGET_DEVICE_FLAG_CANDIDATES LANG DEVICE) 88 if(NOT OpenMPTarget_${LANG}_FLAGS) 89 unset(OpenMPTarget_FLAG_CANDIDATES) 90 91 set(OMPTarget_FLAGS_Clang "-fopenmp-targets=${DEVICE}") 92 set(OMPTarget_FLAGS_GNU "-foffload=${DEVICE}=\"-lm -latomic\"") 93 set(OMPTarget_FLAGS_XL "-qoffload") 94 set(OMPTarget_FLAGS_PGI "-mp=${DEVICE}") 95 set(OMPTarget_FLAGS_NVHPC "-mp=${DEVICE}") 96 97 if(DEFINED OMPTarget_FLAGS_${CMAKE_${LANG}_COMPILER_ID}) 98 set(OpenMPTarget_FLAG_CANDIDATES "${OMPTarget_FLAGS_${CMAKE_${LANG}_COMPILER_ID}}") 99 endif() 100 101 set(OpenMPTarget_${LANG}_FLAG_CANDIDATES "${OpenMPTarget_FLAG_CANDIDATES}" PARENT_SCOPE) 102 else() 103 set(OpenMPTarget_${LANG}_FLAG_CANDIDATES "${OpenMPTarget_${LANG}_FLAGS}" PARENT_SCOPE) 104 endif() 105endfunction() 106 107# Get the coded name of the device for each compiler. 108function(_OPENMP_TARGET_DEVICE_CANDIDATES LANG DEVICE) 109 if (NOT OpenMPTarget_${DEVICE}_DEVICE) 110 unset(OpenMPTarget_DEVICE_CANDIDATES) 111 112 # Check each supported device. 113 if("${DEVICE}" STREQUAL "NVPTX") 114 if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") 115 set(OMPTarget_DEVICE_Clang "nvptx32-nvidia-cuda") 116 else() 117 set(OMPTarget_DEVICE_Clang "nvptx64-nvidia-cuda") 118 endif() 119 set(OMPTarget_DEVICE_GNU "nvptx-none") 120 set(OMPTarget_DEVICE_XL "") 121 set(OMPTarget_DEVICE_PGI "gpu") 122 set(OMPTarget_DEVICE_NVHPC "gpu") 123 124 if(DEFINED OMPTarget_DEVICE_${CMAKE_${LANG}_COMPILER_ID}) 125 set(OpenMPTarget_DEVICE_CANDIDATES "${OMPTarget_DEVICE_${CMAKE_${LANG}_COMPILER_ID}}") 126 endif() 127 elseif("${DEVICE}" STREQUAL "AMDGPU") 128 set(OMPTarget_DEVICE_Clang "amdgcn-amd-amdhsa") 129 set(OMPTarget_DEVICE_GNU "hsa") 130 131 if(DEFINED OMPTarget_DEVICE_${CMAKE_${LANG}_COMPILER_ID}) 132 set(OpenMPTarget_DEVICE_CANDIDATES "${OMPTarget_DEVICE_${CMAKE_${LANG}_COMPILER_ID}}") 133 endif() 134 endif() 135 set(OpenMPTarget_${LANG}_DEVICE_CANDIDATES "${OpenMPTarget_DEVICE_CANDIDATES}" PARENT_SCOPE) 136 else() 137 set(OpenMPTarget_${LANG}_DEVICE_CANDIDATES "${OpenMPTarget_${LANG}_DEVICE}" PARENT_SCOPE) 138 endif() 139endfunction() 140 141# Get flags for setting the device's architecture for each compiler. 142function(_OPENMP_TARGET_DEVICE_ARCH_CANDIDATES LANG DEVICE DEVICE_FLAG) 143 if(OpenMPTarget_${DEVICE}_ARCH) 144 # Only Clang supports selecting the architecture for now. 145 set(OMPTarget_ARCH_Clang "-Xopenmp-target=${DEVICE_FLAG} -march=${OpenMPTarget_${DEVICE}_ARCH}") 146 147 if(DEFINED OMPTarget_ARCH_${CMAKE_${LANG}_COMPILER_ID}) 148 set(OpenMPTarget_DEVICE_ARCH_CANDIDATES "${OMPTarget_ARCH_${CMAKE_${LANG}_COMPILER_ID}}") 149 endif() 150 set(OpenMPTarget_${LANG}_DEVICE_ARCH_CANDIDATES "${OpenMPTarget_DEVICE_ARCH_CANDIDATES}" PARENT_SCOPE) 151 else() 152 set(OpenMPTarget_${LANG}_DEVICE_ARCH_CANDIDATES "" PARENT_SCOPE) 153 endif() 154endfunction() 155 156set(OpenMPTarget_C_CXX_TEST_SOURCE 157"#include <omp.h> 158int main(void) { 159 int isHost; 160#pragma omp target map(from: isHost) 161 { isHost = omp_is_initial_device(); } 162 return isHost; 163}") 164 165function(_OPENMP_TARGET_WRITE_SOURCE_FILE LANG SRC_FILE_CONTENT_VAR SRC_FILE_NAME SRC_FILE_FULLPATH) 166 set(WORK_DIR ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenMPTarget) 167 if("${LANG}" STREQUAL "C") 168 set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.c") 169 file(WRITE "${SRC_FILE}" "${OpenMPTarget_C_CXX_${SRC_FILE_CONTENT_VAR}}") 170 elseif("${LANG}" STREQUAL "CXX") 171 set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.cpp") 172 file(WRITE "${SRC_FILE}" "${OpenMPTarget_C_CXX_${SRC_FILE_CONTENT_VAR}}") 173 endif() 174 set(${SRC_FILE_FULLPATH} "${SRC_FILE}" PARENT_SCOPE) 175endfunction() 176 177# Get the candidate flags and try to compile a test application. If it compiles 178# and all the flags are found, we assume the compiler supports offloading. 179function(_OPENMP_TARGET_DEVICE_GET_FLAGS LANG DEVICE OPENMP_FLAG_VAR OPENMP_LIB_VAR OPENMP_DEVICE_VAR OPENMP_ARCH_VAR) 180 _OPENMP_TARGET_DEVICE_CANDIDATES(${LANG} ${DEVICE}) 181 _OPENMP_TARGET_DEVICE_FLAG_CANDIDATES(${LANG} "${OpenMPTarget_${LANG}_DEVICE_CANDIDATES}") 182 _OPENMP_TARGET_DEVICE_ARCH_CANDIDATES(${LANG} ${DEVICE} "${OpenMPTarget_${LANG}_DEVICE_CANDIDATES}") 183 _OPENMP_TARGET_WRITE_SOURCE_FILE("${LANG}" "TEST_SOURCE" OpenMPTargetTryFlag _OPENMP_TEST_SRC) 184 185 # Try to compile a test application with the found flags. 186 try_compile(OpenMPTarget_COMPILE_RESULT ${CMAKE_BINARY_DIR} ${_OPENMP_TEST_SRC} 187 CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OpenMP_${LANG}_FLAGS} ${OpenMPTarget_${LANG}_FLAG_CANDIDATES} ${OpenMPTarget_${LANG}_DEVICE_ARCH_CANDIDATES}" 188 "-DINCLUDE_DIRECTORIES:STRING=${OpenMP_${LANG}_INCLUDE_DIR}" 189 LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} 190 OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT 191 ) 192 193 file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 194 "Detecting OpenMP ${CMAKE_${LANG}_COMPILER_ID} ${DEVICE} target support with the following Flags: 195 ${OpenMP_${LANG}_FLAGS} ${OpenMPTarget_${LANG}_FLAG_CANDIDATES} ${OpenMPTarget_${LANG}_DEVICE_ARCH_CANDIDATES} 196 With the following output:\n ${OpenMP_TRY_COMPILE_OUTPUT}\n") 197 198 # If compilation was successful and the device was found set the return variables. 199 if (OpenMPTarget_COMPILE_RESULT AND DEFINED OpenMPTarget_${LANG}_DEVICE_CANDIDATES) 200 file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 201 "Compilation successful, adding flags for ${DEVICE}.\n\n") 202 203 # Clang has a separate library for target offloading. 204 if(CMAKE_${LANG}_COMPILER_ID STREQUAL "Clang") 205 find_library(OpenMPTarget_libomptarget_LIBRARY 206 NAMES omptarget 207 HINTS ${CMAKE_${LANG}_IMPLICIT_LINK_DIRECTORIES} 208 ) 209 mark_as_advanced(OpenMPTarget_libomptarget_LIBRARY) 210 set("${OPENMP_LIB_VAR}" "${OpenMPTarget_libomptarget_LIBRARY}" PARENT_SCOPE) 211 else() 212 unset("${OPENMP_LIB_VAR}" PARENT_SCOPE) 213 endif() 214 set("${OPENMP_DEVICE_VAR}" "${OpenMPTarget_${LANG}_DEVICE_CANDIDATES}" PARENT_SCOPE) 215 set("${OPENMP_FLAG_VAR}" "${OpenMPTarget_${LANG}_FLAG_CANDIDATES}" PARENT_SCOPE) 216 set("${OPENMP_ARCH_VAR}" "${OpenMPTarget_${LANG}_DEVICE_ARCH_CANDIDATES}" PARENT_SCOPE) 217 else() 218 unset("${OPENMP_DEVICE_VAR}" PARENT_SCOPE) 219 unset("${OPENMP_FLAG_VAR}" PARENT_SCOPE) 220 unset("${OPENMP_ARCH_VAR}" PARENT_SCOPE) 221 endif() 222endfunction() 223 224# Load the compiler support for each device. 225foreach(LANG IN ITEMS C CXX) 226 # Cache the version in case CMake doesn't load the OpenMP package this time 227 set(OpenMP_${LANG}_VERSION ${OpenMP_${LANG}_VERSION} 228 CACHE STRING "OpenMP Version" FORCE) 229 mark_as_advanced(OpenMP_${LANG}_VERSION) 230 foreach(DEVICE IN ITEMS NVPTX AMDGPU) 231 if(CMAKE_${LANG}_COMPILER_LOADED) 232 if(NOT DEFINED OpenMPTarget_${LANG}_FLAGS OR NOT DEFINED OpenMPTarget_${LANG}_DEVICE) 233 _OPENMP_TARGET_DEVICE_GET_FLAGS(${LANG} ${DEVICE} 234 OpenMPTarget_${DEVICE}_FLAGS_WORK 235 OpenMPTarget_${DEVICE}_LIBS_WORK 236 OpenMPTarget_${DEVICE}_DEVICE_WORK 237 OpenMPTarget_${DEVICE}_ARCHS_WORK) 238 239 separate_arguments(_OpenMPTarget_${DEVICE}_FLAGS NATIVE_COMMAND "${OpenMPTarget_${DEVICE}_FLAGS_WORK}") 240 separate_arguments(_OpenMPTarget_${DEVICE}_ARCHS NATIVE_COMMAND "${OpenMPTarget_${DEVICE}_ARCHS_WORK}") 241 set(OpenMPTarget_${DEVICE}_FLAGS ${_OpenMPTarget_${DEVICE}_FLAGS} 242 CACHE STRING "${DEVICE} target compile flags for OpenMP target offloading" FORCE) 243 set(OpenMPTarget_${DEVICE}_ARCH ${_OpenMPTarget_${DEVICE}_ARCHS} 244 CACHE STRING "${DEVICE} target architecture flags for OpenMP target offloading" FORCE) 245 set(OpenMPTarget_${DEVICE}_LIBRARIES ${OpenMPTarget_${DEVICE}_LIBS_WORK} 246 CACHE STRING "${DEVICE} target libraries for OpenMP target offloading" FORCE) 247 mark_as_advanced(OpenMPTarget_${DEVICE}_FLAGS OpenMPTarget_${DEVICE}_ARCH OpenMPTarget_${DEVICE}_LIBRARIES) 248 endif() 249 endif() 250 endforeach() 251endforeach() 252 253if(OpenMPTarget_FIND_COMPONENTS) 254 set(OpenMPTarget_FINDLIST ${OpenMPTarget_FIND_COMPONENTS}) 255else() 256 set(OpenMPTarget_FINDLIST NVPTX) 257endif() 258 259unset(_OpenMPTarget_MIN_VERSION) 260 261# Attempt to find each requested device. 262foreach(LANG IN ITEMS C CXX) 263 foreach(DEVICE IN LISTS OpenMPTarget_FINDLIST) 264 if(CMAKE_${LANG}_COMPILER_LOADED) 265 set(OpenMPTarget_${DEVICE}_VERSION "${OpenMP_${LANG}_VERSION}") 266 set(OpenMPTarget_${DEVICE}_VERSION_MAJOR "${OpenMP_${LANG}_VERSION}_MAJOR") 267 set(OpenMPTarget_${DEVICE}_VERSION_MINOR "${OpenMP_${LANG}_VERSION}_MINOR") 268 set(OpenMPTarget_${DEVICE}_FIND_QUIETLY ${OpenMPTarget_FIND_QUIETLY}) 269 set(OpenMPTarget_${DEVICE}_FIND_REQUIRED ${OpenMPTarget_FIND_REQUIRED}) 270 set(OpenMPTarget_${DEVICE}_FIND_VERSION ${OpenMPTarget_FIND_VERSION}) 271 set(OpenMPTarget_${DEVICE}_FIND_VERSION_EXACT ${OpenMPTarget_FIND_VERSION_EXACT}) 272 273 # OpenMP target offloading is only supported in OpenMP 4.0 an newer. 274 if(OpenMPTarget_${DEVICE}_VERSION AND ("${OpenMPTarget_${DEVICE}_VERSION}" VERSION_LESS "4.0")) 275 message(SEND_ERROR "FindOpenMPTarget requires at least OpenMP 4.0") 276 endif() 277 278 set(FPHSA_NAME_MISMATCHED TRUE) 279 find_package_handle_standard_args(OpenMPTarget_${DEVICE} 280 REQUIRED_VARS OpenMPTarget_${DEVICE}_FLAGS 281 VERSION_VAR OpenMPTarget_${DEVICE}_VERSION) 282 283 if(OpenMPTarget_${DEVICE}_FOUND) 284 if(DEFINED OpenMPTarget_${DEVICE}_VERSION) 285 if(NOT _OpenMPTarget_MIN_VERSION OR _OpenMPTarget_MIN_VERSION VERSION_GREATER OpenMPTarget_${LANG}_VERSION) 286 set(_OpenMPTarget_MIN_VERSION OpenMPTarget_${DEVICE}_VERSION) 287 endif() 288 endif() 289 # Create a new target. 290 if(NOT TARGET OpenMPTarget::OpenMPTarget_${DEVICE}) 291 add_library(OpenMPTarget::OpenMPTarget_${DEVICE} INTERFACE IMPORTED) 292 endif() 293 # Get compiler flags for offloading to the device and architecture and 294 # set the target features. Include the normal OpenMP flags as well. 295 set_property(TARGET OpenMPTarget::OpenMPTarget_${DEVICE} PROPERTY 296 INTERFACE_COMPILE_OPTIONS 297 "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenMPTarget_${DEVICE}_FLAGS}>" 298 "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenMPTarget_${DEVICE}_ARCH}>" 299 "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenMP_${LANG}_FLAGS}>") 300 set_property(TARGET OpenMPTarget::OpenMPTarget_${DEVICE} PROPERTY 301 INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${OpenMP_${LANG}_INCLUDE_DIRS}>") 302 set_property(TARGET OpenMPTarget::OpenMPTarget_${DEVICE} PROPERTY 303 INTERFACE_LINK_LIBRARIES 304 "${OpenMPTarget_${DEVICE}_LIBRARIES}" 305 "${OpenMP_${LANG}_LIBRARIES}") 306 # The offloading flags must also be passed during the linking phase so 307 # the compiler can pass the binary to the correct toolchain. 308 set_property(TARGET OpenMPTarget::OpenMPTarget_${DEVICE} PROPERTY 309 INTERFACE_LINK_OPTIONS 310 "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenMPTarget_${DEVICE}_FLAGS}>" 311 "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenMPTarget_${DEVICE}_ARCH}>" 312 "$<$<COMPILE_LANGUAGE:${LANG}>:${OpenMP_${LANG}_FLAGS}>") 313 # Combine all the flags if not using the target for convenience. 314 set(OpenMPTarget_${DEVICE}_FLAGS ${OpenMP_${LANG}_FLAGS} 315 ${OpenMPTarget_${DEVICE}_FLAGS} 316 ${OpenMPTarget_${DEVICE}_ARCH} 317 CACHE STRING "${DEVICE} target compile flags for OpenMP target offloading" FORCE) 318 endif() 319 endif() 320 endforeach() 321endforeach() 322 323unset(_OpenMPTarget_REQ_VARS) 324foreach(DEVICE IN LISTS OpenMPTarget_FINDLIST) 325 list(APPEND _OpenMPTarget_REQ_VARS "OpenMPTarget_${DEVICE}_FOUND") 326endforeach() 327 328find_package_handle_standard_args(OpenMPTarget 329 REQUIRED_VARS ${_OpenMPTarget_REQ_VARS} 330 VERSION_VAR ${_OpenMPTarget_MIN_VERSION} 331 HANDLE_COMPONENTS) 332 333if(NOT (CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED) OR CMAKE_Fortran_COMPILER_LOADED) 334 message(SEND_ERROR "FindOpenMPTarget requires the C or CXX languages to be enabled") 335endif() 336 337unset(OpenMPTarget_C_CXX_TEST_SOURCE) 338cmake_policy(POP) 339