1# Compiles an OpenCL C - or assembles an LL file - to bytecode 2# 3# Arguments: 4# * TRIPLE <string> 5# Target triple for which to compile the bytecode file. 6# * INPUT <string> 7# File to compile/assemble to bytecode 8# * OUTPUT <string> 9# Bytecode file to generate 10# * EXTRA_OPTS <string> ... 11# List of compiler options to use. Note that some are added by default. 12# * DEPENDENCIES <string> ... 13# List of extra dependencies to inject 14# 15# Depends on the clang, llvm-as, and llvm-link targets for compiling, 16# assembling, and linking, respectively. 17function(compile_to_bc) 18 cmake_parse_arguments(ARG 19 "" 20 "TRIPLE;INPUT;OUTPUT" 21 "EXTRA_OPTS;DEPENDENCIES" 22 ${ARGN} 23 ) 24 25 # If this is an LLVM IR file (identified soley by its file suffix), 26 # pre-process it with clang to a temp file, then assemble that to bytecode. 27 set( TMP_SUFFIX ) 28 get_filename_component( FILE_EXT ${ARG_INPUT} EXT ) 29 if( NOT ${FILE_EXT} STREQUAL ".ll" ) 30 # Pass '-c' when not running the preprocessor 31 set( PP_OPTS -c ) 32 else() 33 set( PP_OPTS -E;-P ) 34 set( TMP_SUFFIX .tmp ) 35 endif() 36 37 set( TARGET_ARG ) 38 if( ARG_TRIPLE ) 39 set( TARGET_ARG "-target" ${ARG_TRIPLE} ) 40 endif() 41 42 # Ensure the directory we are told to output to exists 43 get_filename_component( ARG_OUTPUT_DIR ${ARG_OUTPUT} DIRECTORY ) 44 file( MAKE_DIRECTORY ${ARG_OUTPUT_DIR} ) 45 46 add_custom_command( 47 OUTPUT ${ARG_OUTPUT}${TMP_SUFFIX} 48 COMMAND ${clang_exe} 49 ${TARGET_ARG} 50 ${PP_OPTS} 51 ${ARG_EXTRA_OPTS} 52 -MD -MF ${ARG_OUTPUT}.d -MT ${ARG_OUTPUT}${TMP_SUFFIX} 53 # LLVM 13 enables standard includes by default - we don't want 54 # those when pre-processing IR. We disable it unconditionally. 55 $<$<VERSION_GREATER_EQUAL:${LLVM_PACKAGE_VERSION},13.0.0>:-cl-no-stdinc> 56 -emit-llvm 57 -o ${ARG_OUTPUT}${TMP_SUFFIX} 58 -x cl 59 ${ARG_INPUT} 60 DEPENDS 61 ${clang_target} 62 ${ARG_INPUT} 63 ${ARG_DEPENDENCIES} 64 DEPFILE ${ARG_OUTPUT}.d 65 ) 66 67 if( ${FILE_EXT} STREQUAL ".ll" ) 68 add_custom_command( 69 OUTPUT ${ARG_OUTPUT} 70 COMMAND ${llvm-as_exe} -o ${ARG_OUTPUT} ${ARG_OUTPUT}${TMP_SUFFIX} 71 DEPENDS ${llvm-as_target} ${ARG_OUTPUT}${TMP_SUFFIX} 72 ) 73 endif() 74endfunction() 75 76# Links together one or more bytecode files 77# 78# Arguments: 79# * INTERNALIZE 80# Set if -internalize flag should be passed when linking 81# * TARGET <string> 82# Custom target to create 83# * INPUT <string> ... 84# List of bytecode files to link together 85# * DEPENDENCIES <string> ... 86# List of extra dependencies to inject 87function(link_bc) 88 cmake_parse_arguments(ARG 89 "INTERNALIZE" 90 "TARGET" 91 "INPUTS;DEPENDENCIES" 92 ${ARGN} 93 ) 94 95 set( LINK_INPUT_ARG ${ARG_INPUTS} ) 96 if( WIN32 OR CYGWIN ) 97 # Create a response file in case the number of inputs exceeds command-line 98 # character limits on certain platforms. 99 file( TO_CMAKE_PATH ${LIBCLC_ARCH_OBJFILE_DIR}/${ARG_TARGET}.rsp RSP_FILE ) 100 # Turn it into a space-separate list of input files 101 list( JOIN ARG_INPUTS " " RSP_INPUT ) 102 file( GENERATE OUTPUT ${RSP_FILE} CONTENT ${RSP_INPUT} ) 103 # Ensure that if this file is removed, we re-run CMake 104 set_property( DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS 105 ${RSP_FILE} 106 ) 107 set( LINK_INPUT_ARG "@${RSP_FILE}" ) 108 endif() 109 110 add_custom_command( 111 OUTPUT ${ARG_TARGET}.bc 112 COMMAND ${llvm-link_exe} $<$<BOOL:${ARG_INTERNALIZE}>:--internalize> -o ${ARG_TARGET}.bc ${LINK_INPUT_ARG} 113 DEPENDS ${llvm-link_target} ${ARG_DEPENDENCIES} ${ARG_INPUTS} ${RSP_FILE} 114 ) 115 116 add_custom_target( ${ARG_TARGET} ALL DEPENDS ${ARG_TARGET}.bc ) 117 set_target_properties( ${ARG_TARGET} PROPERTIES 118 TARGET_FILE ${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}.bc 119 FOLDER "libclc/Device IR/Linking" 120 ) 121endfunction() 122 123# Decomposes and returns variables based on a libclc triple and architecture 124# combination. Returns data via one or more optional output variables. 125# 126# Arguments: 127# * TRIPLE <string> 128# libclc target triple to query 129# * DEVICE <string> 130# libclc device to query 131# 132# Optional Arguments: 133# * CPU <var> 134# Variable name to be set to the target CPU 135# * ARCH_SUFFIX <var> 136# Variable name to be set to the triple/architecture suffix 137# * CLANG_TRIPLE <var> 138# Variable name to be set to the normalized clang triple 139function(get_libclc_device_info) 140 cmake_parse_arguments(ARG 141 "" 142 "TRIPLE;DEVICE;CPU;ARCH_SUFFIX;CLANG_TRIPLE" 143 "" 144 ${ARGN} 145 ) 146 147 if( NOT ARG_TRIPLE OR NOT ARG_DEVICE ) 148 message( FATAL_ERROR "Must provide both TRIPLE and DEVICE" ) 149 endif() 150 151 string( REPLACE "-" ";" TRIPLE ${ARG_TRIPLE} ) 152 list( GET TRIPLE 0 ARCH ) 153 154 # Some targets don't have a specific device architecture to target 155 if( ARG_DEVICE STREQUAL none OR ARCH STREQUAL spirv OR ARCH STREQUAL spirv64 ) 156 set( cpu ) 157 set( arch_suffix "${ARG_TRIPLE}" ) 158 else() 159 set( cpu "${ARG_DEVICE}" ) 160 set( arch_suffix "${ARG_DEVICE}-${ARG_TRIPLE}" ) 161 endif() 162 163 if( ARG_CPU ) 164 set( ${ARG_CPU} ${cpu} PARENT_SCOPE ) 165 endif() 166 167 if( ARG_ARCH_SUFFIX ) 168 set( ${ARG_ARCH_SUFFIX} ${arch_suffix} PARENT_SCOPE ) 169 endif() 170 171 # Some libclc targets are not real clang triples: return their canonical 172 # triples. 173 if( ARCH STREQUAL spirv OR ARCH STREQUAL clspv ) 174 set( ARG_TRIPLE "spir--" ) 175 elseif( ARCH STREQUAL spirv64 OR ARCH STREQUAL clspv64 ) 176 set( ARG_TRIPLE "spir64--" ) 177 endif() 178 179 if( ARG_CLANG_TRIPLE ) 180 set( ${ARG_CLANG_TRIPLE} ${ARG_TRIPLE} PARENT_SCOPE ) 181 endif() 182endfunction() 183 184# Compiles a list of library source files (provided by LIB_FILES/GEN_FILES) and 185# compiles them to LLVM bytecode (or SPIR-V), links them together and optimizes 186# them. 187# 188# For bytecode libraries, a list of ALIASES may optionally be provided to 189# produce additional symlinks. 190# 191# Arguments: 192# * ARCH <string> 193# libclc architecture being built 194# * ARCH_SUFFIX <string> 195# libclc architecture/triple suffix 196# * TRIPLE <string> 197# Triple used to compile 198# 199# Optional Arguments: 200# * CLC_INTERNAL 201# Pass if compiling the internal CLC builtin libraries, which are not 202# optimized and do not have aliases created. 203# * LIB_FILES <string> ... 204# List of files that should be built for this library 205# * GEN_FILES <string> ... 206# List of generated files (in build dir) that should be built for this library 207# * COMPILE_FLAGS <string> ... 208# Compilation options (for clang) 209# * OPT_FLAGS <string> ... 210# Optimization options (for opt) 211# * ALIASES <string> ... 212# List of aliases 213# * INTERNAL_LINK_DEPENDENCIES <string> ... 214# A list of extra bytecode files to link into the builtin library. Symbols 215# from these link dependencies will be internalized during linking. 216function(add_libclc_builtin_set) 217 cmake_parse_arguments(ARG 218 "CLC_INTERNAL" 219 "ARCH;TRIPLE;ARCH_SUFFIX" 220 "LIB_FILES;GEN_FILES;COMPILE_FLAGS;OPT_FLAGS;ALIASES;INTERNAL_LINK_DEPENDENCIES" 221 ${ARGN} 222 ) 223 224 if( NOT ARG_ARCH OR NOT ARG_ARCH_SUFFIX OR NOT ARG_TRIPLE ) 225 message( FATAL_ERROR "Must provide ARCH, ARCH_SUFFIX, and TRIPLE" ) 226 endif() 227 228 set( bytecode_files "" ) 229 foreach( file IN LISTS ARG_GEN_FILES ARG_LIB_FILES ) 230 # We need to take each file and produce an absolute input file, as well 231 # as a unique architecture-specific output file. We deal with a mix of 232 # different input files, which makes this trickier. 233 if( ${file} IN_LIST ARG_GEN_FILES ) 234 # Generated files are given just as file names, which we must make 235 # absolute to the binary directory. 236 set( input_file ${CMAKE_CURRENT_BINARY_DIR}/${file} ) 237 set( output_file "${LIBCLC_ARCH_OBJFILE_DIR}/${file}.bc" ) 238 else() 239 # Other files are originally relative to each SOURCE file, which are 240 # then make relative to the libclc root directory. We must normalize 241 # the path (e.g., ironing out any ".."), then make it relative to the 242 # root directory again, and use that relative path component for the 243 # binary path. 244 get_filename_component( abs_path ${file} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR} ) 245 file( RELATIVE_PATH root_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${abs_path} ) 246 set( input_file ${CMAKE_CURRENT_SOURCE_DIR}/${file} ) 247 set( output_file "${LIBCLC_ARCH_OBJFILE_DIR}/${root_rel_path}.bc" ) 248 endif() 249 250 get_filename_component( file_dir ${file} DIRECTORY ) 251 252 compile_to_bc( 253 TRIPLE ${ARG_TRIPLE} 254 INPUT ${input_file} 255 OUTPUT ${output_file} 256 EXTRA_OPTS -fno-builtin -nostdlib 257 "${ARG_COMPILE_FLAGS}" -I${CMAKE_CURRENT_SOURCE_DIR}/${file_dir} 258 DEPENDENCIES generate_convert.cl clspv-generate_convert.cl 259 ) 260 list( APPEND bytecode_files ${output_file} ) 261 endforeach() 262 263 set( builtins_comp_lib_tgt builtins.comp.${ARG_ARCH_SUFFIX} ) 264 add_custom_target( ${builtins_comp_lib_tgt} 265 DEPENDS ${bytecode_files} 266 ) 267 set_target_properties( ${builtins_comp_lib_tgt} PROPERTIES FOLDER "libclc/Device IR/Comp" ) 268 269 if( NOT bytecode_files ) 270 message(FATAL_ERROR "Cannot create an empty builtins library") 271 endif() 272 273 set( builtins_link_lib_tgt builtins.link.${ARG_ARCH_SUFFIX} ) 274 275 if( NOT ARG_INTERNAL_LINK_DEPENDENCIES ) 276 link_bc( 277 TARGET ${builtins_link_lib_tgt} 278 INPUTS ${bytecode_files} 279 DEPENDENCIES ${builtins_comp_lib_tgt} 280 ) 281 else() 282 # If we have libraries to link while internalizing their symbols, we need 283 # two separate link steps; the --internalize flag applies to all link 284 # inputs but the first. 285 set( builtins_link_lib_tmp_tgt builtins.link.pre-deps.${ARG_ARCH_SUFFIX} ) 286 link_bc( 287 TARGET ${builtins_link_lib_tmp_tgt} 288 INPUTS ${bytecode_files} 289 DEPENDENCIES ${builtins_comp_lib_tgt} 290 ) 291 link_bc( 292 INTERNALIZE 293 TARGET ${builtins_link_lib_tgt} 294 INPUTS $<TARGET_PROPERTY:${builtins_link_lib_tmp_tgt},TARGET_FILE> 295 ${ARG_INTERNAL_LINK_DEPENDENCIES} 296 DEPENDENCIES ${builtins_link_lib_tmp_tgt} 297 ) 298 endif() 299 300 # For the CLC internal builtins, exit here - we only optimize the targets' 301 # entry points once we've linked the CLC buitins into them 302 if( ARG_CLC_INTERNAL ) 303 return() 304 endif() 305 306 set( builtins_link_lib $<TARGET_PROPERTY:${builtins_link_lib_tgt},TARGET_FILE> ) 307 308 if( ARG_ARCH STREQUAL spirv OR ARG_ARCH STREQUAL spirv64 ) 309 set( spv_suffix ${ARG_ARCH_SUFFIX}.spv ) 310 add_custom_command( OUTPUT ${spv_suffix} 311 COMMAND ${llvm-spirv_exe} ${spvflags} -o ${spv_suffix} ${builtins_link_lib} 312 DEPENDS ${llvm-spirv_target} ${builtins_link_lib} ${builtins_link_lib_tgt} 313 ) 314 add_custom_target( "prepare-${spv_suffix}" ALL DEPENDS "${spv_suffix}" ) 315 set_target_properties( "prepare-${spv_suffix}" PROPERTIES FOLDER "libclc/Device IR/Prepare" ) 316 install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${spv_suffix} 317 DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" ) 318 319 return() 320 endif() 321 322 set( builtins_opt_lib_tgt builtins.opt.${ARG_ARCH_SUFFIX} ) 323 324 # Add opt target 325 add_custom_command( OUTPUT ${builtins_opt_lib_tgt}.bc 326 COMMAND ${opt_exe} ${ARG_OPT_FLAGS} -o ${builtins_opt_lib_tgt}.bc 327 ${builtins_link_lib} 328 DEPENDS ${opt_target} ${builtins_link_lib} ${builtins_link_lib_tgt} 329 ) 330 add_custom_target( ${builtins_opt_lib_tgt} 331 ALL DEPENDS ${builtins_opt_lib_tgt}.bc 332 ) 333 set_target_properties( ${builtins_opt_lib_tgt} PROPERTIES 334 TARGET_FILE ${CMAKE_CURRENT_BINARY_DIR}/${builtins_opt_lib_tgt}.bc 335 FOLDER "libclc/Device IR/Opt" 336 ) 337 338 set( builtins_opt_lib $<TARGET_PROPERTY:${builtins_opt_lib_tgt},TARGET_FILE> ) 339 340 # Add prepare target 341 set( obj_suffix ${ARG_ARCH_SUFFIX}.bc ) 342 add_custom_command( OUTPUT ${obj_suffix} 343 COMMAND ${prepare_builtins_exe} -o ${obj_suffix} ${builtins_opt_lib} 344 DEPENDS ${builtins_opt_lib} ${builtins_opt_lib_tgt} ${prepare_builtins_target} ) 345 add_custom_target( prepare-${obj_suffix} ALL DEPENDS ${obj_suffix} ) 346 set_target_properties( "prepare-${obj_suffix}" PROPERTIES FOLDER "libclc/Device IR/Prepare" ) 347 348 # nvptx-- targets don't include workitem builtins 349 if( NOT ARG_TRIPLE MATCHES ".*ptx.*--$" ) 350 add_test( NAME external-calls-${obj_suffix} 351 COMMAND ./check_external_calls.sh ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} ${LLVM_TOOLS_BINARY_DIR} 352 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) 353 endif() 354 355 install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" ) 356 foreach( a ${ARG_ALIASES} ) 357 set( alias_suffix "${a}-${ARG_TRIPLE}.bc" ) 358 add_custom_command( 359 OUTPUT ${alias_suffix} 360 COMMAND ${CMAKE_COMMAND} -E create_symlink ${obj_suffix} ${alias_suffix} 361 DEPENDS prepare-${obj_suffix} ) 362 add_custom_target( alias-${alias_suffix} ALL DEPENDS ${alias_suffix} ) 363 set_target_properties( alias-${alias_suffix} PROPERTIES FOLDER "libclc/Device IR/Aliases" ) 364 install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${alias_suffix} 365 DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" ) 366 endforeach( a ) 367endfunction(add_libclc_builtin_set) 368 369# Produces a list of libclc source files by walking over SOURCES files in a 370# given directory. Outputs the list of files in LIB_FILE_LIST. 371# 372# LIB_FILE_LIST may be pre-populated and is appended to. 373# 374# Arguments: 375# * CLC_INTERNAL 376# Pass if compiling the internal CLC builtin libraries, which have a 377# different directory structure. 378# * LIB_ROOT_DIR <string> 379# Root directory containing target's lib files, relative to libclc root 380# directory. If not provided, is set to '.'. 381# * DIRS <string> ... 382# List of directories under LIB_ROOT_DIR to walk over searching for SOURCES 383# files 384function(libclc_configure_lib_source LIB_FILE_LIST) 385 cmake_parse_arguments(ARG 386 "CLC_INTERNAL" 387 "LIB_ROOT_DIR" 388 "DIRS" 389 ${ARGN} 390 ) 391 392 if( NOT ARG_LIB_ROOT_DIR ) 393 set(ARG_LIB_ROOT_DIR ".") 394 endif() 395 396 # Enumerate SOURCES* files 397 set( source_list ) 398 foreach( l ${ARG_DIRS} ) 399 foreach( s "SOURCES" "SOURCES_${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}" ) 400 if( ARG_CLC_INTERNAL ) 401 file( TO_CMAKE_PATH ${ARG_LIB_ROOT_DIR}/lib/${l}/${s} file_loc ) 402 else() 403 file( TO_CMAKE_PATH ${ARG_LIB_ROOT_DIR}/${l}/lib/${s} file_loc ) 404 endif() 405 file( TO_CMAKE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${file_loc} loc ) 406 # Prepend the location to give higher priority to 407 # specialized implementation 408 if( EXISTS ${loc} ) 409 set( source_list ${file_loc} ${source_list} ) 410 endif() 411 endforeach() 412 endforeach() 413 414 ## Add the generated convert files here to prevent adding the ones listed in 415 ## SOURCES 416 set( rel_files ${${LIB_FILE_LIST}} ) # Source directory input files, relative to the root dir 417 set( objects ${${LIB_FILE_LIST}} ) # A "set" of already-added input files 418 419 foreach( l ${source_list} ) 420 file( READ ${l} file_list ) 421 string( REPLACE "\n" ";" file_list ${file_list} ) 422 get_filename_component( dir ${l} DIRECTORY ) 423 foreach( f ${file_list} ) 424 # Only add each file once, so that targets can 'specialize' builtins 425 if( NOT ${f} IN_LIST objects ) 426 list( APPEND objects ${f} ) 427 list( APPEND rel_files ${dir}/${f} ) 428 endif() 429 endforeach() 430 endforeach() 431 432 set( ${LIB_FILE_LIST} ${rel_files} PARENT_SCOPE ) 433endfunction(libclc_configure_lib_source LIB_FILE_LIST) 434