1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
#
# Copyright (c) 2020-2025, Intel Corporation
#
# SPDX-License-Identifier: BSD-3-Clause
if (ISPC_BUILD)
set (ISPC_EXECUTABLE $<TARGET_FILE:ispc>)
else()
find_program (ISPC_EXECUTABLE ispc)
if (NOT ISPC_EXECUTABLE)
message(FATAL_ERROR "Failed to find ispc" )
endif()
endif()
# Identify host arch
if(UNIX)
execute_process(COMMAND sh "-c" "uname -m | sed -e s/x86_64/x86/ -e s/i686/x86/ -e s/arm.*/arm/ -e s/sa110/arm/" OUTPUT_VARIABLE ARCH)
string(STRIP ${ARCH} ARCH)
execute_process(COMMAND getconf LONG_BIT OUTPUT_VARIABLE ARCH_BIT)
string(STRIP ${ARCH_BIT} ARCH_BIT)
if("${ARCH}" STREQUAL "x86")
if(${ARCH_BIT} EQUAL 32)
set(ISPC_ARCH "x86")
else()
set(ISPC_ARCH "x86-64")
endif()
elseif("${ARCH}" STREQUAL "arm")
if(${ARCH_BIT} EQUAL 32)
set(ISPC_ARCH "arm")
else()
set(ISPC_ARCH "aarch64")
endif()
elseif("${ARCH}" STREQUAL "aarch64")
set(ISPC_ARCH "aarch64")
else()
message(FATAL_ERROR "Cannot detect host architecture for benchamrks: ${ARCH}.")
endif()
else()
set(ARCH "x86")
if(CMAKE_SIZEOF_VOID_P EQUAL 8 )
set(ISPC_ARCH "x86-64")
else()
set(ISPC_ARCH "x86")
endif()
endif()
# Suffixes for multi-target compilation (x86 only)
set(ISPC_KNOWN_TARGETS "sse2" "sse4" "avx1" "avx2" "avx512skx")
#######################
# add_ispc_to_target
#######################
#
# Adds a ISPC compilation custom command associated with an existing
# target and sets a dependancy on that new command.
#
# TARGET : Name of the target to add ISPC to.
# CPP_MAIN_FILE : Main cpp file which includes ispc headers
# SOURCES : List of ISPC source files.
#
function(add_ispc_to_target)
set(options)
set(one_value_args
TARGET
CPP_MAIN_FILE
)
set(multi_value_args
SOURCES
)
cmake_parse_arguments("ADD_ISPC"
"${options}"
"${one_value_args}"
"${multi_value_args}"
${ARGN}
)
set(ISPC_DST_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/CMakeFiles/ispc/")
file(TO_NATIVE_PATH "${ISPC_DST_DIR}" ISPC_DST_DIR)
file(MAKE_DIRECTORY ${ISPC_DST_DIR})
string(FIND ${BENCHMARKS_ISPC_TARGETS} "," MULTI_TARGET)
foreach(ISPC_SRC_FILE ${ADD_ISPC_SOURCES})
set(ISPC_TARGET_HEADERS "")
set(ISPC_TARGET_OBJS "")
# Full path to source file
get_filename_component(SRC_LOCATION "${ISPC_SRC_FILE}" ABSOLUTE "${CMAKE_CURRENT_SOURCE_DIR}")
# Construct names for header and object files
string(REPLACE ".ispc" "${CMAKE_CXX_OUTPUT_EXTENSION}" ISPC_OBJ "${ISPC_SRC_FILE}")
set(ISPC_OBJ "${ISPC_DST_DIR}${ISPC_OBJ}")
string(REPLACE ".ispc" "_ispc.h" ISPC_HEADER "${ISPC_SRC_FILE}")
set(ISPC_HEADER "${ISPC_DST_DIR}${ISPC_HEADER}")
list(APPEND ISPC_TARGET_OBJS "${ISPC_OBJ}")
list(APPEND ISPC_TARGET_HEADERS "${ISPC_HEADER}")
# Collect list of expected outputs in case of multiple targets
if(${MULTI_TARGET} GREATER -1)
foreach (ISPC_TARGET ${ISPC_KNOWN_TARGETS})
string(FIND ${BENCHMARKS_ISPC_TARGETS} ${ISPC_TARGET} FOUND_TARGET)
if(${FOUND_TARGET} GREATER -1)
set(OUTPUT_TARGET ${ISPC_TARGET})
if (${ISPC_TARGET} STREQUAL "avx1")
set(OUTPUT_TARGET "avx")
endif()
string(REPLACE ".ispc" "_${OUTPUT_TARGET}${CMAKE_CXX_OUTPUT_EXTENSION}" ISPC_TARGET_OBJ ${ISPC_SRC_FILE})
set(ISPC_TARGET_OBJ ${ISPC_DST_DIR}${ISPC_TARGET_OBJ})
list(APPEND ISPC_TARGET_OBJS ${ISPC_TARGET_OBJ})
string(REPLACE ".ispc" "_ispc_${OUTPUT_TARGET}.h" ISPC_TARGET_HEADER ${ISPC_SRC_FILE})
set(ISPC_TARGET_HEADER ${ISPC_DST_DIR}${ISPC_TARGET_HEADER})
list(APPEND ISPC_TARGET_HEADERS ${ISPC_TARGET_HEADER})
endif()
endforeach()
endif()
if(UNIX)
set(ISPC_PIC "--pic")
endif()
# Passing space separate string yields escaped spaces.
# So convert to a list and then use generator expression, i.e. "$<JOIN:${FLAGS},;>"
separate_arguments(FLAGS NATIVE_COMMAND ${BENCHMARKS_ISPC_FLAGS})
# Pass HAS_FP16 definition to ISPC compiler
if(HAS_FP16_SUPPORT)
set(ISPC_FP16_FLAG "-DHAS_FP16")
else()
set(ISPC_FP16_FLAG "")
endif()
set(INTRINSICS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../../stdlib/include)
add_custom_command(
OUTPUT ${ISPC_TARGET_OBJS} ${ISPC_TARGET_HEADERS}
COMMENT "Compiling ${ISPC_SRC_FILE} for ${BENCHMARKS_ISPC_TARGETS} target(s)"
COMMAND ${ISPC_EXECUTABLE} ${SRC_LOCATION} -o ${ISPC_OBJ} -h ${ISPC_HEADER} --arch=${ISPC_ARCH} --target=${BENCHMARKS_ISPC_TARGETS} ${ISPC_PIC} ${ISPC_FP16_FLAG} -I${INTRINSICS_HEADERS} "$<JOIN:${FLAGS},;>"
DEPENDS ${ISPC_EXECUTABLE} ${ISPC_DEPS}
DEPENDS ${ISPC_SRC_FILE}
COMMAND_EXPAND_LISTS
)
if(MSVC)
# Add .ispc file to VS solution.
target_sources(${ADD_ISPC_TARGET} PUBLIC ${SRC_LOCATION})
# Group .ispc files inside Visual Studio
source_group("ISPC" FILES ${SRC_LOCATION})
# Group benchmarks in "Benchmarks" folder
set_target_properties(${ADD_ISPC_TARGET} PROPERTIES FOLDER "Benchmarks")
endif()
set_source_files_properties(${ISPC_TARGET_OBJS} PROPERTIES GENERATED TRUE EXTERNAL_OBJECT TRUE)
set_source_files_properties(${ISPC_TARGET_HEADERS} PROPERTIES GENERATED TRUE EXTERNAL_OBJECT TRUE)
list(APPEND ISPC_HEADERS_LIST ${SRC_LOCATION} ${ISPC_TARGET_HEADERS})
list(APPEND ISPC_OBJS_LIST ${ISPC_TARGET_OBJS})
endforeach()
set_property (SOURCE ${ADD_ISPC_CPP_MAIN_FILE} PROPERTY OBJECT_DEPENDS ${ISPC_HEADERS_LIST})
target_include_directories(${ADD_ISPC_TARGET} PRIVATE ${ISPC_DST_DIR})
if (X86_HOST)
target_compile_definitions(${ADD_ISPC_TARGET} PRIVATE IS_X86_ARCH)
endif()
target_compile_definitions(${ADD_ISPC_TARGET} PRIVATE ISPC_ENABLED)
target_link_libraries(${ADD_ISPC_TARGET} PRIVATE ${ISPC_OBJS_LIST})
endfunction()
# A macro to add a benchmark
macro(compile_benchmark_test name)
add_executable(${name} "")
# aligned_alloc() requires C++17
set_target_properties(${name} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES)
add_ispc_to_target(
TARGET ${name}
CPP_MAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp
SOURCES ${name}.ispc)
target_sources(
${name}
PRIVATE ${name}.cpp)
target_compile_definitions(
${name}
PRIVATE BENCHMARKS_ISPC_TARGETS=\"${BENCHMARKS_ISPC_TARGETS}\"
BENCHMARKS_ISPC_FLAGS=\"${BENCHMARKS_ISPC_FLAGS}\")
if(${ISPC_ARCH} MATCHES "x86|x86-64")
# Turn on AVX2 support in the C++ compiler to be able to use AVX2 intrinsics.
if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
add_compile_options(-mavx2)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
add_compile_options(/QxAVX2)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_compile_options(/arch:AVX2)
endif()
endif()
# To enable google benchmarks:
target_link_libraries(${name} PRIVATE benchmark)
get_filename_component(INSTALL_SUBFOLDER "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
install(
TARGETS ${name}
RUNTIME DESTINATION "benchmarks/${INSTALL_SUBFOLDER}")
add_test(NAME ${name}_test COMMAND ${name} --benchmark_min_time=0.01)
add_dependencies(${BENCHMARKS_PROJECT_NAME} ${name})
endmacro(compile_benchmark_test)
|