# Compiles an OpenCL C - or assembles an LL file - to bytecode # # Arguments: # * TRIPLE # Target triple for which to compile the bytecode file. # * INPUT # File to compile/assemble to bytecode # * OUTPUT # Bytecode file to generate # * EXTRA_OPTS ... # List of compiler options to use. Note that some are added by default. # * DEPENDENCIES ... # List of extra dependencies to inject # # Depends on the libclc::clang and libclc::llvm-as targets for compiling and # assembling, respectively. function(compile_to_bc) cmake_parse_arguments(ARG "" "TRIPLE;INPUT;OUTPUT" "EXTRA_OPTS;DEPENDENCIES" ${ARGN} ) # If this is an LLVM IR file (identified soley by its file suffix), # pre-process it with clang to a temp file, then assemble that to bytecode. set( TMP_SUFFIX ) get_filename_component( FILE_EXT ${ARG_INPUT} EXT ) if( NOT ${FILE_EXT} STREQUAL ".ll" ) # Pass '-c' when not running the preprocessor set( PP_OPTS -c ) else() set( PP_OPTS -E;-P ) set( TMP_SUFFIX .tmp ) endif() set( TARGET_ARG ) if( ARG_TRIPLE ) set( TARGET_ARG "-target" ${ARG_TRIPLE} ) endif() add_custom_command( OUTPUT ${ARG_OUTPUT}${TMP_SUFFIX} COMMAND libclc::clang ${TARGET_ARG} ${PP_OPTS} ${ARG_EXTRA_OPTS} -MD -MF ${ARG_OUTPUT}.d -MT ${ARG_OUTPUT}${TMP_SUFFIX} # LLVM 13 enables standard includes by default - we don't want # those when pre-processing IR. We disable it unconditionally. $<$:-cl-no-stdinc> -emit-llvm -o ${ARG_OUTPUT}${TMP_SUFFIX} -x cl ${ARG_INPUT} DEPENDS libclc::clang ${ARG_INPUT} ${ARG_DEPENDENCIES} DEPFILE ${ARG_OUTPUT}.d ) if( ${FILE_EXT} STREQUAL ".ll" ) add_custom_command( OUTPUT ${ARG_OUTPUT} COMMAND libclc::llvm-as -o ${ARG_OUTPUT} ${ARG_OUTPUT}${TMP_SUFFIX} DEPENDS libclc::llvm-as ${ARG_OUTPUT}${TMP_SUFFIX} ) endif() endfunction() # Links together one or more bytecode files # # Arguments: # * TARGET # Custom target to create # * INPUT ... # List of bytecode files to link together function(link_bc) cmake_parse_arguments(ARG "" "TARGET" "INPUTS" ${ARGN} ) add_custom_command( OUTPUT ${ARG_TARGET}.bc COMMAND libclc::llvm-link -o ${ARG_TARGET}.bc ${ARG_INPUTS} DEPENDS libclc::llvm-link ${ARG_INPUTS} ) add_custom_target( ${ARG_TARGET} ALL DEPENDS ${ARG_TARGET}.bc ) set_target_properties( ${ARG_TARGET} PROPERTIES TARGET_FILE ${ARG_TARGET}.bc ) endfunction() # Decomposes and returns variables based on a libclc triple and architecture # combination. Returns data via one or more optional output variables. # # Arguments: # * TRIPLE # libclc target triple to query # * DEVICE # libclc device to query # # Optional Arguments: # * CPU # Variable name to be set to the target CPU # * ARCH_SUFFIX # Variable name to be set to the triple/architecture suffix # * CLANG_TRIPLE # Variable name to be set to the normalized clang triple function(get_libclc_device_info) cmake_parse_arguments(ARG "" "TRIPLE;DEVICE;CPU;ARCH_SUFFIX;CLANG_TRIPLE" "" ${ARGN} ) if( NOT ARG_TRIPLE OR NOT ARG_DEVICE ) message( FATAL_ERROR "Must provide both TRIPLE and DEVICE" ) endif() string( REPLACE "-" ";" TRIPLE ${ARG_TRIPLE} ) list( GET TRIPLE 0 ARCH ) # Some targets don't have a specific device architecture to target if( ARG_DEVICE STREQUAL none OR ARCH STREQUAL spirv OR ARCH STREQUAL spirv64 ) set( cpu ) set( arch_suffix "${ARG_TRIPLE}" ) else() set( cpu "${ARG_DEVICE}" ) set( arch_suffix "${ARG_DEVICE}-${ARG_TRIPLE}" ) endif() if( ARG_CPU ) set( ${ARG_CPU} ${cpu} PARENT_SCOPE ) endif() if( ARG_ARCH_SUFFIX ) set( ${ARG_ARCH_SUFFIX} ${arch_suffix} PARENT_SCOPE ) endif() # Some libclc targets are not real clang triples: return their canonical # triples. if( ARCH STREQUAL spirv OR ARCH STREQUAL clspv ) set( ARG_TRIPLE "spir--" ) elseif( ARCH STREQUAL spirv64 OR ARCH STREQUAL clspv64 ) set( ARG_TRIPLE "spir64--" ) endif() if( ARG_CLANG_TRIPLE ) set( ${ARG_CLANG_TRIPLE} ${ARG_TRIPLE} PARENT_SCOPE ) endif() endfunction()