1 cmake_minimum_required(VERSION 3.20.0)
3 if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
4 project(libclc VERSION 0.2.0 LANGUAGES CXX C)
6 set(LLVM_SUBPROJECT_TITLE "libclc")
8 set(CMAKE_CXX_STANDARD 17)
10 # Add path for custom modules
11 list( INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" )
13 set( LIBCLC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )
14 set( LIBCLC_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR} )
15 set( LIBCLC_OBJFILE_DIR ${LIBCLC_BINARY_DIR}/obj.libclc.dir )
19 include( GNUInstallDirs )
20 set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
21 amdgcn-amdhsa/lib/SOURCES;
23 amdgcn-mesa3d/lib/SOURCES;
29 ptx-nvidiacl/lib/SOURCES;
35 set( LIBCLC_MIN_LLVM 3.9.0 )
37 set( LIBCLC_TARGETS_TO_BUILD "all"
38 CACHE STRING "Semicolon-separated list of libclc targets to build, or 'all'." )
40 option( ENABLE_RUNTIME_SUBNORMAL "Enable runtime linking of subnormal support." OFF )
42 if( LIBCLC_STANDALONE_BUILD OR CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
43 # Out-of-tree configuration
44 set( LIBCLC_STANDALONE_BUILD TRUE )
46 find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_DIR}")
49 message( STATUS "libclc LLVM version: ${LLVM_PACKAGE_VERSION}" )
51 if( LLVM_PACKAGE_VERSION VERSION_LESS LIBCLC_MIN_LLVM )
52 message( FATAL_ERROR "libclc needs at least LLVM ${LIBCLC_MIN_LLVM}" )
55 # Import required tools
56 if( NOT EXISTS ${LIBCLC_CUSTOM_LLVM_TOOLS_BINARY_DIR} )
57 foreach( tool IN ITEMS clang llvm-as llvm-link opt )
58 find_program( LLVM_TOOL_${tool} ${tool} PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
59 set( ${tool}_exe ${LLVM_TOOL_${tool}} )
64 # In-tree configuration
65 set( LIBCLC_STANDALONE_BUILD FALSE )
67 set( LLVM_PACKAGE_VERSION ${LLVM_VERSION} )
69 # Note that we check this later (for both build types) but we can provide a
70 # more useful error message when built in-tree. We assume that LLVM tools are
71 # always available so don't warn here.
72 if( NOT clang IN_LIST LLVM_ENABLE_PROJECTS )
73 message(FATAL_ERROR "Clang is not enabled, but is required to build libclc in-tree")
76 if( NOT EXISTS ${LIBCLC_CUSTOM_LLVM_TOOLS_BINARY_DIR} )
77 setup_host_tool( clang CLANG clang_exe clang_target )
78 setup_host_tool( llvm-as LLVM_AS llvm-as_exe llvm-as_target )
79 setup_host_tool( llvm-link LLVM_LINK llvm-link_exe llvm-link_target )
80 setup_host_tool( opt OPT opt_exe opt_target )
84 if( EXISTS ${LIBCLC_CUSTOM_LLVM_TOOLS_BINARY_DIR} )
85 message( WARNING "Using custom LLVM tools to build libclc: "
86 "${LIBCLC_CUSTOM_LLVM_TOOLS_BINARY_DIR}, "
87 " ensure the tools are up to date." )
88 # Note - use a differently named variable than LLVM_TOOL_${tool} as above, as
89 # the variable name is used to cache the result of find_program. If we used
90 # the same name, a user wouldn't be able to switch a build between default
92 foreach( tool IN ITEMS clang llvm-as llvm-link opt )
93 find_program( LLVM_CUSTOM_TOOL_${tool} ${tool}
94 PATHS ${LIBCLC_CUSTOM_LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
95 set( ${tool}_exe ${LLVM_CUSTOM_TOOL_${tool}} )
100 foreach( tool IN ITEMS clang opt llvm-as llvm-link )
101 if( NOT EXISTS "${${tool}_exe}" AND NOT TARGET "${${tool}_target}" )
102 message( FATAL_ERROR "libclc toolchain incomplete - missing tool ${tool}!" )
106 # llvm-spirv is an optional dependency, used to build spirv-* targets.
107 find_program( LLVM_SPIRV llvm-spirv PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
110 add_executable( libclc::llvm-spirv IMPORTED GLOBAL )
111 set_target_properties( libclc::llvm-spirv PROPERTIES IMPORTED_LOCATION ${LLVM_SPIRV} )
114 # List of all targets. Note that some are added dynamically below.
115 set( LIBCLC_TARGETS_ALL
127 # mesa3d environment is only available since LLVM 4.0
128 if( LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 4.0.0 )
129 list( APPEND LIBCLC_TARGETS_ALL amdgcn-mesa-mesa3d )
132 # spirv-mesa3d and spirv64-mesa3d targets can only be built with the (optional)
133 # llvm-spirv external tool.
134 if( TARGET libclc::llvm-spirv )
135 list( APPEND LIBCLC_TARGETS_ALL spirv-mesa3d- spirv64-mesa3d- )
138 if( LIBCLC_TARGETS_TO_BUILD STREQUAL "all" )
139 set( LIBCLC_TARGETS_TO_BUILD ${LIBCLC_TARGETS_ALL} )
142 list( SORT LIBCLC_TARGETS_TO_BUILD )
144 # Verify that the user hasn't requested mesa3d targets without an available
146 if( "spirv-mesa3d-" IN_LIST LIBCLC_TARGETS_TO_BUILD OR "spirv64-mesa3d-" IN_LIST LIBCLC_TARGETS_TO_BUILD )
147 if( NOT TARGET libclc::llvm-spirv )
148 message( FATAL_ERROR "SPIR-V targets requested, but spirv-tools is not installed" )
152 # Construct LLVM version define
153 set( LLVM_VERSION_DEFINE "-DHAVE_LLVM=0x${LLVM_VERSION_MAJOR}0${LLVM_VERSION_MINOR}" )
155 # This needs to be set before any target that needs it
156 # We need to use LLVM_INCLUDE_DIRS here, because if we are linking to an
157 # llvm build directory, this includes $src/llvm/include which is where all the
158 # headers are not $build/include/ which is what LLVM_INCLUDE_DIR is set to.
159 include_directories( ${LLVM_INCLUDE_DIRS} )
161 # Setup prepare_builtins tools
162 set(LLVM_LINK_COMPONENTS
169 if( LIBCLC_STANDALONE_BUILD )
170 add_llvm_executable( prepare_builtins utils/prepare-builtins.cpp )
171 set( prepare_builtins_exe prepare_builtins )
172 set( prepare_builtins_target prepare_builtins )
174 add_llvm_utility( prepare_builtins utils/prepare-builtins.cpp )
175 setup_host_tool( prepare_builtins PREPARE_BUILTINS prepare_builtins_exe prepare_builtins_target )
177 target_compile_definitions( prepare_builtins PRIVATE ${LLVM_VERSION_DEFINE} )
178 # These were not properly reported in early LLVM and we don't need them
179 target_compile_options( prepare_builtins PRIVATE -fno-rtti -fno-exceptions )
182 set( r600--_devices cedar cypress barts cayman )
183 set( amdgcn--_devices tahiti )
184 set( amdgcn-mesa-mesa3d_devices ${amdgcn--_devices} )
185 set( amdgcn--amdhsa_devices none )
186 set( clspv--_devices none )
187 set( clspv64--_devices none )
188 set( nvptx--_devices none )
189 set( nvptx64--_devices none )
190 set( nvptx--nvidiacl_devices none )
191 set( nvptx64--nvidiacl_devices none )
192 set( spirv-mesa3d-_devices none )
193 set( spirv64-mesa3d-_devices none )
196 set( cedar_aliases palm sumo sumo2 redwood juniper )
197 set( cypress_aliases hemlock )
198 set( barts_aliases turks caicos )
199 set( cayman_aliases aruba )
200 set( tahiti_aliases pitcairn verde oland hainan bonaire kabini kaveri hawaii
201 mullins tonga tongapro iceland carrizo fiji stoney polaris10 polaris11
203 gfx900 gfx902 gfx904 gfx906 gfx908 gfx909 gfx90a gfx90c gfx940 gfx941 gfx942
204 gfx1010 gfx1011 gfx1012 gfx1013
205 gfx1030 gfx1031 gfx1032 gfx1033 gfx1034 gfx1035 gfx1036
206 gfx1100 gfx1101 gfx1102 gfx1103
207 gfx1150 gfx1151 gfx1152
212 configure_file( libclc.pc.in libclc.pc @ONLY )
213 install( FILES ${CMAKE_CURRENT_BINARY_DIR}/libclc.pc DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" )
214 install( DIRECTORY generic/include/clc DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" )
216 if( ENABLE_RUNTIME_SUBNORMAL )
217 foreach( file IN ITEMS subnormal_use_default subnormal_disable )
220 INPUTS ${CMAKE_CURRENT_SOURCE_DIR}/generic/lib/${file}.ll
222 install( FILES $<TARGET_PROPERTY:${file},TARGET_FILE> ARCHIVE
223 DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
227 find_package( Python3 REQUIRED COMPONENTS Interpreter )
228 file( TO_CMAKE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/generic/lib/gen_convert.py script_loc )
231 COMMAND ${Python3_EXECUTABLE} ${script_loc} > convert.cl
232 DEPENDS ${script_loc} )
233 add_custom_target( "generate_convert.cl" DEPENDS convert.cl )
234 set_target_properties( "generate_convert.cl" PROPERTIES FOLDER "libclc/Sourcegenning" )
237 OUTPUT clspv-convert.cl
238 COMMAND ${Python3_EXECUTABLE} ${script_loc} --clspv > clspv-convert.cl
239 DEPENDS ${script_loc} )
240 add_custom_target( "clspv-generate_convert.cl" DEPENDS clspv-convert.cl )
241 set_target_properties( "clspv-generate_convert.cl" PROPERTIES FOLDER "libclc/Sourcegenning" )
245 foreach( t ${LIBCLC_TARGETS_TO_BUILD} )
246 message( STATUS "libclc target '${t}' is enabled" )
247 string( REPLACE "-" ";" TRIPLE ${t} )
248 list( GET TRIPLE 0 ARCH )
249 list( GET TRIPLE 1 VENDOR )
250 list( GET TRIPLE 2 OS )
254 if ( NOT ${ARCH} STREQUAL spirv AND NOT ${ARCH} STREQUAL spirv64 AND
255 NOT ${ARCH} STREQUAL clspv AND NOT ${ARCH} STREQUAL clspv64)
256 LIST( APPEND dirs generic )
259 if( ${ARCH} STREQUAL r600 OR ${ARCH} STREQUAL amdgcn )
260 list( APPEND dirs amdgpu )
264 if( ${ARCH} STREQUAL nvptx OR ${ARCH} STREQUAL nvptx64 )
270 # Enumerate SOURCES* files
272 foreach( l ${dirs} ${DARCH} ${DARCH}-${OS} ${DARCH}-${VENDOR}-${OS} )
273 foreach( s "SOURCES" "SOURCES_${LLVM_MAJOR}.${LLVM_MINOR}" )
274 file( TO_CMAKE_PATH ${l}/lib/${s} file_loc )
275 file( TO_CMAKE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${file_loc} loc )
276 # Prepend the location to give higher priority to
277 # specialized implementation
279 set( source_list ${file_loc} ${source_list} )
284 # Add the generated convert.cl here to prevent adding the one listed in
286 set( objects ) # A "set" of already-added input files
287 set( rel_files ) # Source directory input files, relative to the root dir
288 set( gen_files ) # Generated binary input files, relative to the binary dir
289 if( NOT ${ARCH} STREQUAL "spirv" AND NOT ${ARCH} STREQUAL "spirv64" )
290 if( NOT ENABLE_RUNTIME_SUBNORMAL AND NOT ${ARCH} STREQUAL "clspv" AND
291 NOT ${ARCH} STREQUAL "clspv64" )
292 list( APPEND gen_files convert.cl )
293 list( APPEND objects convert.cl )
294 list( APPEND rel_files generic/lib/subnormal_use_default.ll )
295 elseif(${ARCH} STREQUAL "clspv" OR ${ARCH} STREQUAL "clspv64")
296 list( APPEND gen_files clspv-convert.cl )
297 list( APPEND objects clspv-convert.cl )
301 foreach( l ${source_list} )
302 file( READ ${l} file_list )
303 string( REPLACE "\n" ";" file_list ${file_list} )
304 get_filename_component( dir ${l} DIRECTORY )
305 foreach( f ${file_list} )
306 # Only add each file once, so that targets can 'specialize' builtins
307 if( NOT ${f} IN_LIST objects )
308 list( APPEND objects ${f} )
309 list( APPEND rel_files ${dir}/${f} )
314 foreach( d ${${t}_devices} )
315 get_libclc_device_info(
319 ARCH_SUFFIX arch_suffix
320 CLANG_TRIPLE clang_triple
324 if( NOT "${cpu}" STREQUAL "" )
325 set( mcpu "-mcpu=${cpu}" )
328 message( STATUS " device: ${d} ( ${${d}_aliases} )" )
330 if ( ARCH STREQUAL spirv OR ARCH STREQUAL spirv64 )
331 set( build_flags -O0 -finline-hint-functions )
333 set( spvflags --spirv-max-version=1.1 )
334 elseif( ARCH STREQUAL clspv OR ARCH STREQUAL clspv64 )
335 set( build_flags "-Wno-unknown-assumption")
342 set( LIBCLC_ARCH_OBJFILE_DIR "${LIBCLC_OBJFILE_DIR}/${arch_suffix}" )
343 file( MAKE_DIRECTORY ${LIBCLC_ARCH_OBJFILE_DIR} )
345 string( TOUPPER "CLC_${ARCH}" CLC_TARGET_DEFINE )
347 list( APPEND build_flags
349 -D${CLC_TARGET_DEFINE}
350 -I${CMAKE_CURRENT_SOURCE_DIR}/generic/include
351 # FIXME: Fix libclc to not require disabling this noisy warning
352 -Wno-bitwise-conditional-parentheses
355 set( bytecode_files "" )
356 foreach( file IN LISTS gen_files rel_files )
357 # We need to take each file and produce an absolute input file, as well
358 # as a unique architecture-specific output file. We deal with a mix of
359 # different input files, which makes this trickier.
360 if( ${file} IN_LIST gen_files )
361 # Generated files are given just as file names, which we must make
362 # absolute to the binary directory.
363 set( input_file ${CMAKE_CURRENT_BINARY_DIR}/${file} )
364 set( output_file "${LIBCLC_ARCH_OBJFILE_DIR}/${file}.bc" )
366 # Other files are originally relative to each SOURCE file, which are
367 # then make relative to the libclc root directory. We must normalize
368 # the path (e.g., ironing out any ".."), then make it relative to the
369 # root directory again, and use that relative path component for the
371 get_filename_component( abs_path ${file} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )
372 file( RELATIVE_PATH root_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${abs_path} )
373 set( input_file ${CMAKE_CURRENT_SOURCE_DIR}/${file} )
374 set( output_file "${LIBCLC_ARCH_OBJFILE_DIR}/${root_rel_path}.bc" )
377 get_filename_component( file_dir ${file} DIRECTORY )
380 TRIPLE ${clang_triple}
382 OUTPUT ${output_file}
383 EXTRA_OPTS "${mcpu}" -fno-builtin -nostdlib
384 "${build_flags}" -I${CMAKE_CURRENT_SOURCE_DIR}/${file_dir}
385 DEPENDENCIES generate_convert.cl clspv-generate_convert.cl
387 list( APPEND bytecode_files ${output_file} )
390 set( builtins_comp_lib_tgt builtins.comp.${arch_suffix} )
391 add_custom_target( ${builtins_comp_lib_tgt}
392 DEPENDS ${bytecode_files}
394 set_target_properties( ${builtins_comp_lib_tgt} PROPERTIES FOLDER "libclc/Device IR/Comp" )
396 set( builtins_link_lib_tgt builtins.link.${arch_suffix} )
398 TARGET ${builtins_link_lib_tgt}
399 INPUTS ${bytecode_files}
400 DEPENDENCIES ${builtins_comp_lib_tgt}
403 set( builtins_link_lib $<TARGET_PROPERTY:${builtins_link_lib_tgt},TARGET_FILE> )
405 if( ARCH STREQUAL spirv OR ARCH STREQUAL spirv64 )
406 set( spv_suffix ${arch_suffix}.spv )
407 add_custom_command( OUTPUT ${spv_suffix}
408 COMMAND libclc::llvm-spirv ${spvflags} -o ${spv_suffix} ${builtins_link_lib}
409 DEPENDS ${builtins_link_lib} ${builtins_link_lib_tgt}
411 add_custom_target( "prepare-${spv_suffix}" ALL DEPENDS "${spv_suffix}" )
412 set_target_properties( "prepare-${spv_suffix}" PROPERTIES FOLDER "libclc/Device IR/Prepare" )
413 install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${spv_suffix}
414 DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
416 set( builtins_opt_lib_tgt builtins.opt.${arch_suffix} )
419 add_custom_command( OUTPUT ${builtins_opt_lib_tgt}.bc
420 COMMAND ${opt_exe} ${opt_flags} -o ${builtins_opt_lib_tgt}.bc
422 DEPENDS ${opt_target} ${builtins_link_lib} ${builtins_link_lib_tgt}
424 add_custom_target( ${builtins_opt_lib_tgt}
425 ALL DEPENDS ${builtins_opt_lib_tgt}.bc
427 set_target_properties( ${builtins_opt_lib_tgt}
428 PROPERTIES TARGET_FILE ${builtins_opt_lib_tgt}.bc
429 FOLDER "libclc/Device IR/Opt"
432 set( builtins_opt_lib $<TARGET_PROPERTY:${builtins_opt_lib_tgt},TARGET_FILE> )
435 set( obj_suffix ${arch_suffix}.bc )
436 add_custom_command( OUTPUT ${obj_suffix}
437 COMMAND ${prepare_builtins_exe} -o ${obj_suffix} ${builtins_opt_lib}
438 DEPENDS ${builtins_opt_lib} ${builtins_opt_lib_tgt} ${prepare_builtins_target} )
439 add_custom_target( prepare-${obj_suffix} ALL DEPENDS ${obj_suffix} )
440 set_target_properties( "prepare-${obj_suffix}" PROPERTIES FOLDER "libclc/Device IR/Prepare" )
442 # nvptx-- targets don't include workitem builtins
443 if( NOT clang_triple MATCHES ".*ptx.*--$" )
444 add_test( NAME external-calls-${obj_suffix}
445 COMMAND ./check_external_calls.sh ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} ${LLVM_TOOLS_BINARY_DIR}
446 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
449 install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
450 foreach( a ${${d}_aliases} )
451 set( alias_suffix "${a}-${clang_triple}.bc" )
452 add_custom_target( ${alias_suffix} ALL
453 COMMAND ${CMAKE_COMMAND} -E create_symlink ${obj_suffix} ${alias_suffix}
454 DEPENDS prepare-${obj_suffix} )
455 set_target_properties( "${alias_suffix}" PROPERTIES FOLDER "libclc/Device IR/Aliases" )
456 install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${alias_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )