| # Adds source files to a libclc builtin library target with deduplication. If a | |
| # source with the same basename already exists in the target's SOURCES property | |
| # the new file is skipped. This enables target-specific directories to override | |
| # generic implementations when they are included first. | |
| # | |
| # Sources are specified as paths relative to CMAKE_CURRENT_SOURCE_DIR, or | |
| # relative to BASE_DIR if provided. | |
| function(libclc_add_sources target) | |
| cmake_parse_arguments(ARG "" "BASE_DIR" "FILES" ${ARGN}) | |
| if(NOT ARG_BASE_DIR) | |
| set(ARG_BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) | |
| endif() | |
| get_target_property(existing ${target} SOURCES) | |
| set(seen) | |
| foreach(file IN LISTS existing) | |
| get_filename_component(name "${file}" NAME) | |
| list(APPEND seen "${name}") | |
| endforeach() | |
| set(new_sources) | |
| foreach(rel_src IN LISTS ARG_FILES) | |
| get_filename_component(name "${rel_src}" NAME) | |
| if(NOT name IN_LIST seen) | |
| list(APPEND new_sources "${ARG_BASE_DIR}/${rel_src}") | |
| list(APPEND seen "${name}") | |
| endif() | |
| endforeach() | |
| if(new_sources) | |
| target_sources(${target} PRIVATE ${new_sources}) | |
| set(inc_dirs) | |
| foreach(file IN LISTS new_sources) | |
| get_filename_component(dir "${file}" DIRECTORY) | |
| list(APPEND inc_dirs "${dir}") | |
| endforeach() | |
| list(REMOVE_DUPLICATES inc_dirs) | |
| target_include_directories(${target} PRIVATE ${inc_dirs}) | |
| endif() | |
| endfunction() | |
| # Appends a compile option to the given source files. Source paths are | |
| # relative to CMAKE_CURRENT_SOURCE_DIR. The property is set in the | |
| # top-level libclc directory scope. | |
| function(libclc_set_source_options option) | |
| set(srcs ${ARGN}) | |
| list(TRANSFORM srcs PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") | |
| set_property(SOURCE ${srcs} | |
| DIRECTORY ${LIBCLC_SOURCE_DIR} | |
| APPEND PROPERTY COMPILE_OPTIONS ${option} | |
| ) | |
| endfunction() | |
| # Creates an object library target for libclc builtins and configures its | |
| # compile options, include directories, and definitions. Subdirectories | |
| # populate sources via libclc_add_sources() after this call. | |
| function(libclc_add_builtin_library target_name) | |
| cmake_parse_arguments(ARG | |
| "" | |
| "FOLDER;SOURCE_SUB_DIR" | |
| "COMPILE_OPTIONS;INCLUDE_DIRS;COMPILE_DEFINITIONS" | |
| ${ARGN} | |
| ) | |
| if(NOT ARG_SOURCE_SUB_DIR) | |
| message(FATAL_ERROR "SOURCE_SUB_DIR is required for libclc_add_builtin_library") | |
| endif() | |
| add_library(${target_name} OBJECT) | |
| target_compile_options(${target_name} PRIVATE ${ARG_COMPILE_OPTIONS}) | |
| target_include_directories(${target_name} PRIVATE ${ARG_INCLUDE_DIRS}) | |
| target_compile_definitions(${target_name} PRIVATE ${ARG_COMPILE_DEFINITIONS}) | |
| set_target_properties(${target_name} PROPERTIES FOLDER ${ARG_FOLDER}) | |
| # Populate sources. | |
| add_subdirectory( | |
| ${LIBCLC_SOURCE_DIR}/${ARG_SOURCE_SUB_DIR} | |
| ${CMAKE_CURRENT_BINARY_DIR}/${target_name} | |
| ) | |
| endfunction() | |
| # Builds a builtin library from sources, links it with any internalized | |
| # dependencies, produces the final output file, and registers it for | |
| # installation. | |
| function(libclc_add_library target_name) | |
| cmake_parse_arguments(ARG | |
| "" | |
| "ARCH;TRIPLE;TARGET_TRIPLE;SOURCE_TARGET;SOURCE_SUB_DIR;OUTPUT_FILENAME;PARENT_TARGET" | |
| "COMPILE_OPTIONS;INCLUDE_DIRS;COMPILE_DEFINITIONS;INTERNALIZE_LIBRARIES;OPT_FLAGS" | |
| ${ARGN} | |
| ) | |
| if(NOT ARG_SOURCE_TARGET) | |
| message(FATAL_ERROR "SOURCE_TARGET is required for libclc_add_library") | |
| endif() | |
| if(NOT ARG_OUTPUT_FILENAME) | |
| message(FATAL_ERROR "OUTPUT_FILENAME is required for libclc_add_library") | |
| endif() | |
| if(NOT ARG_PARENT_TARGET) | |
| message(FATAL_ERROR "PARENT_TARGET is required for libclc_add_library") | |
| endif() | |
| libclc_add_builtin_library(${ARG_SOURCE_TARGET} | |
| COMPILE_OPTIONS ${ARG_COMPILE_OPTIONS} | |
| INCLUDE_DIRS ${ARG_INCLUDE_DIRS} | |
| COMPILE_DEFINITIONS ${ARG_COMPILE_DEFINITIONS} | |
| SOURCE_SUB_DIR ${ARG_SOURCE_SUB_DIR} | |
| FOLDER "libclc/Device IR/Intermediate" | |
| ) | |
| set(library_dir ${LIBCLC_OUTPUT_LIBRARY_DIR}/${ARG_TARGET_TRIPLE}) | |
| file(MAKE_DIRECTORY ${library_dir}) | |
| # Create a combined static archive from all object libraries for installation. | |
| set(archive_target ${target_name}.a) | |
| add_library(${archive_target} STATIC) | |
| target_link_libraries(${archive_target} PRIVATE | |
| ${ARG_SOURCE_TARGET} ${ARG_INTERNALIZE_LIBRARIES} | |
| ) | |
| set_target_properties(${archive_target} PROPERTIES | |
| OUTPUT_NAME ${ARG_OUTPUT_FILENAME} | |
| PREFIX "" | |
| ARCHIVE_OUTPUT_DIRECTORY ${library_dir} | |
| FOLDER "libclc/Device IR/Library" | |
| ) | |
| # Link the object files, using a temporary library for internalization. | |
| set(linked_bc ${CMAKE_CURRENT_BINARY_DIR}/${target_name}.linked.bc) | |
| set(link_cmd ${llvm-link_exe}) | |
| set(link_deps ${llvm-link_target}) | |
| add_library(${target_name}-src STATIC) | |
| target_link_libraries(${target_name}-src PRIVATE ${ARG_SOURCE_TARGET}) | |
| set_target_properties(${target_name}-src PROPERTIES | |
| FOLDER "libclc/Device IR/Library" | |
| ) | |
| list(APPEND link_cmd $<TARGET_FILE:${target_name}-src>) | |
| list(APPEND link_deps ${target_name}-src) | |
| if(ARG_INTERNALIZE_LIBRARIES) | |
| list(APPEND link_cmd --internalize --only-needed) | |
| foreach(lib ${ARG_INTERNALIZE_LIBRARIES}) | |
| add_library(${target_name}-${lib} STATIC) | |
| target_link_libraries(${target_name}-${lib} PRIVATE ${lib}) | |
| set_target_properties(${target_name}-${lib} PROPERTIES | |
| FOLDER "libclc/Device IR/Library" | |
| ) | |
| list(APPEND link_cmd $<TARGET_FILE:${target_name}-${lib}>) | |
| list(APPEND link_deps ${target_name}-${lib}) | |
| endforeach() | |
| endif() | |
| list(APPEND link_cmd -o ${linked_bc}) | |
| add_custom_command(OUTPUT ${linked_bc} | |
| COMMAND ${link_cmd} | |
| DEPENDS ${link_deps} | |
| ) | |
| string(REPLACE "-" ";" triple_parts "${ARG_TRIPLE}") | |
| list(GET triple_parts 2 triple_os) | |
| if(ARG_ARCH IN_LIST LIBCLC_ARCHS_SPIRV AND NOT triple_os STREQUAL vulkan) | |
| # SPIR-V targets produce a .spv file from the linked bitcode. | |
| set(builtins_lib ${library_dir}/${ARG_OUTPUT_FILENAME}.spv) | |
| if(LIBCLC_USE_SPIRV_BACKEND) | |
| add_custom_command(OUTPUT ${builtins_lib} | |
| COMMAND ${CMAKE_CLC_COMPILER} -c --target=${ARG_TRIPLE} | |
| -mllvm --spirv-ext=+SPV_KHR_fma | |
| -x ir -o ${builtins_lib} ${linked_bc} | |
| DEPENDS ${linked_bc} | |
| ) | |
| else() | |
| add_custom_command(OUTPUT ${builtins_lib} | |
| COMMAND ${llvm-spirv_exe} | |
| --spirv-max-version=1.1 | |
| --spirv-ext=+SPV_KHR_fma | |
| -o ${builtins_lib} ${linked_bc} | |
| DEPENDS ${linked_bc} | |
| ) | |
| endif() | |
| else() | |
| # All other targets produce an optimized .bc file. | |
| set(builtins_lib ${library_dir}/${ARG_OUTPUT_FILENAME}.bc) | |
| add_custom_command(OUTPUT ${builtins_lib} | |
| COMMAND ${opt_exe} ${ARG_OPT_FLAGS} -o ${builtins_lib} ${linked_bc} | |
| DEPENDS ${opt_target} ${linked_bc} | |
| ) | |
| endif() | |
| add_custom_target(${target_name} ALL DEPENDS ${builtins_lib}) | |
| set_target_properties(${target_name} PROPERTIES | |
| TARGET_FILE ${builtins_lib} | |
| FOLDER "libclc/Device IR/Library" | |
| ) | |
| add_dependencies(${ARG_PARENT_TARGET} ${target_name} ${archive_target}) | |
| install(FILES ${builtins_lib} $<TARGET_FILE:${archive_target}> | |
| DESTINATION ${LIBCLC_INSTALL_DIR}/${ARG_TARGET_TRIPLE} | |
| COMPONENT ${ARG_PARENT_TARGET} | |
| ) | |
| endfunction() |