diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b462c45d..48e43ad50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # For GCC: `cmake -B build . && cmake --build build` # For MSVC: `cmake -B build . && cmake --build build --config Release` # You can also use the following options and variables -# - COMPUTE_BACKEND: Set to `cpu`, `cuda`, or `mps` to select the backend +# - COMPUTE_BACKEND: Set to `cpu`, `cuda`, `mps`, or `xpu` to select the backend # - CUDA_VERSION: The expected CUDA version, for sanity checking. The actual version # is whatever CMake finds on your path. # - COMPUTE_CAPABILITY: Which GPU Arch/Compute codes to provide to NVCC. @@ -30,8 +30,8 @@ set(METAL_FILES csrc/mps_kernels.metal) # C++ sources are always included list(APPEND SRC_FILES ${CPP_FILES}) -set(COMPUTE_BACKEND "cpu" CACHE STRING "The compute backend to use (cpu, cuda, mps)") -set_property(CACHE COMPUTE_BACKEND PROPERTY STRINGS cpu cuda mps) +set(COMPUTE_BACKEND "cpu" CACHE STRING "The compute backend to use (cpu, cuda, mps, xpu)") +set_property(CACHE COMPUTE_BACKEND PROPERTY STRINGS cpu cuda mps xpu) option(PTXAS_VERBOSE "Pass through -v flag to PTX Assembler" OFF) if(APPLE) @@ -48,17 +48,28 @@ if(${COMPUTE_BACKEND} STREQUAL "cuda") endif() set(BUILD_CUDA ON) set(BUILD_MPS OFF) + set(BUILD_XPU OFF) elseif(${COMPUTE_BACKEND} STREQUAL "mps") if(NOT APPLE) message(FATAL_ERROR "MPS is only supported on macOS" ) endif() set(BUILD_CUDA OFF) set(BUILD_MPS ON) + set(BUILD_XPU OFF) +elseif(${COMPUTE_BACKEND} STREQUAL "xpu") + if(APPLE) + message(FATAL_ERROR "XPU is not supported on macOS" ) + endif() + set(BUILD_CUDA OFF) + set(BUILD_MPS OFF) + set(BUILD_XPU ON) else() set(BUILD_CUDA OFF) set(BUILD_MPS OFF) + set(BUILD_XPU OFF) endif() +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules) if(BUILD_CUDA) # NVCC normally will only work with MSVC up to 1939. VS2022 17.10+ starts using versions 1940+. @@ -179,6 +190,14 @@ elseif(BUILD_MPS) COMMENT "Compiling Metal kernels" VERBATIM) add_custom_target(metallib DEPENDS "bitsandbytes/bitsandbytes.metallib") +elseif(BUILD_XPU) + include(${PROJECT_SOURCE_DIR}/cmake/XPU.cmake) + include(${PROJECT_SOURCE_DIR}/cmake/XPUBuildFlags.cmake) + + if(BUILD_TEST) + # Test SYCL building system + add_subdirectory(${PROJECT_SOURCE_DIR}/tests/sycl ${CMAKE_BINARY_DIR}/test_sycl) + endif() else() string(APPEND BNB_OUTPUT_NAME "_cpu") set(GPU_SOURCES) diff --git a/cmake/Modules/FindSYCL.cmake b/cmake/Modules/FindSYCL.cmake new file mode 100644 index 000000000..86457ba36 --- /dev/null +++ b/cmake/Modules/FindSYCL.cmake @@ -0,0 +1,539 @@ +#.rst: +# FindSYCL +# -------- +# +# .. note:: + +# The following variables affect the behavior of the macros in the script needed +# to be defined before calling ``SYCL_ADD_EXECUTABLE`` or ``SYCL_ADD_LIBRARY``:: +# +# SYCL_COMPILER +# -- SYCL compiler's executable. +# +# SYCL_FLAGS +# -- SYCL compiler's compilation command line arguments. +# +# SYCL_HOST_FLAGS +# -- SYCL compiler's 3rd party host compiler (e.g. gcc) arguments . +# +# SYCL_DEVICE_LINK_FLAGS +# -- Arguments used when linking device object. +# +# SYCL_OFFLINE_COMPILER_FLAGS +# -- Arguments used by offline compiler at AOT compilation. +# +# SYCL_INCLUDE_DIR +# -- Include directory for SYCL compiler/runtime headers. +# +# SYCL_LIBRARY_DIR +# -- Include directory for SYCL compiler/runtime libraries. + +# Helpers:: +# Introduce SYCL compiler to build .cpp containing SYCL kernel. +# +# SYCL_ADD_EXECUTABLE +# +# SYCL_ADD_LIBRARY + +macro(SYCL_FIND_HELPER_FILE _name _extension) + set(_full_name "${_name}.${_extension}") + # CMAKE_CURRENT_LIST_FILE contains the full path to the file currently being + # processed. Using this variable, we can pull out the current path, and + # provide a way to get access to the other files we need local to here. + get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + set(SYCL_${_name} "${CMAKE_CURRENT_LIST_DIR}/FindSYCL/${_full_name}") + if(NOT EXISTS "${SYCL_${_name}}") + set(error_message "${_full_name} not found in ${CMAKE_CURRENT_LIST_DIR}/FindSYCL") + message(FATAL_ERROR "${error_message}") + endif() + # Set this variable as internal, so the user isn't bugged with it. + set(SYCL_${_name} ${SYCL_${_name}} CACHE INTERNAL "Location of ${_full_name}" FORCE) +endmacro() + +# SYCL_HOST_COMPILER +set(SYCL_HOST_COMPILER "${CMAKE_CXX_COMPILER}" + CACHE FILEPATH "Host side compiler used by SYCL") + +# SYCL_EXECUTABLE +if(SYCL_COMPILER) + set(SYCL_EXECUTABLE ${SYCL_COMPILER} CACHE FILEPATH "SYCL compiler") +else() + if(WIN32) + set(SYCL_EXECUTABLE_NAME icx) + else() + set(SYCL_EXECUTABLE_NAME icpx) + endif() + find_program(SYCL_EXECUTABLE + NAMES ${SYCL_EXECUTABLE_NAME} + PATHS "${SYCL_PACKAGE_DIR}" + PATH_SUFFIXES bin bin64 + NO_DEFAULT_PATH + ) +endif() + +# Parse HOST_COMPILATION mode. +option(SYCL_HOST_COMPILATION_CXX "Generated file extension" ON) + +# SYCL_VERBOSE_BUILD +option(SYCL_VERBOSE_BUILD "Print out the commands run while compiling the SYCL source file. With the Makefile generator this defaults to VERBOSE variable specified on the command line, but can be forced on with this option." OFF) + +macro(SYCL_INCLUDE_EXTERNAL_DEPENDENCIES dependency_file) + list(APPEND SYCL_EXTERNAL_DEPEND ${dependency_file}) +endmacro() + +macro(SYCL_INCLUDE_DEPENDENCIES dependency_file) + set(SYCL_DEPEND) + set(SYCL_DEPEND_REGENERATE FALSE) + + # Make the output depend on the dependency file itself, which should cause the + # rule to re-run. + if(NOT EXISTS ${dependency_file}) + file(WRITE ${dependency_file} "#FindSYCL.cmake generated file. Do not edit.\n") + endif() + + # Always include this file to force CMake to run again next + # invocation and rebuild the dependencies. + include(${dependency_file}) + + if(SYCL_DEPEND) + foreach(f ${SYCL_DEPEND}) + if(NOT EXISTS ${f}) + set(SYCL_DEPEND_REGENERATE TRUE) + endif() + endforeach() + else() + set(SYCL_DEPEND_REGENERATE TRUE) + endif() + + if(SYCL_DEPEND_REGENERATE) + set(SYCL_DEPEND ${dependency_file}) + file(WRITE ${dependency_file} "#FindCUDA.cmake generated file. Do not edit.\n") + endif() +endmacro() + +sycl_find_helper_file(make2cmake cmake) +sycl_find_helper_file(run_sycl cmake) + +macro(SYCL_GET_SOURCES_AND_OPTIONS _sycl_sources _cxx_sources _cmake_options) + set(${_cmake_options}) + set(${_sycl_sources}) + set(${_cxx_sources}) + set(_found_options FALSE) + set(_found_sycl_sources FALSE) + set(_found_cpp_sources FALSE) + foreach(arg ${ARGN}) + if("x${arg}" STREQUAL "xOPTIONS") + set(_found_options TRUE) + set(_found_sycl_sources FALSE) + set(_found_cpp_sources FALSE) + elseif( + "x${arg}" STREQUAL "xEXCLUDE_FROM_ALL" OR + "x${arg}" STREQUAL "xSTATIC" OR + "x${arg}" STREQUAL "xSHARED" OR + "x${arg}" STREQUAL "xMODULE" + ) + list(APPEND ${_cmake_options} ${arg}) + elseif("x${arg}" STREQUAL "xSYCL_SOURCES") + set(_found_options FALSE) + set(_found_sycl_sources TRUE) + set(_found_cpp_sources FALSE) + elseif("x${arg}" STREQUAL "xCXX_SOURCES") + set(_found_options FALSE) + set(_found_sycl_sources FALSE) + set(_found_cpp_sources TRUE) + else() + if(_found_options) + message(FATAL_ERROR "sycl_add_executable/library doesn't support OPTIONS keyword.") + elseif(_found_sycl_sources) + list(APPEND ${_sycl_sources} ${arg}) + elseif(_found_cpp_sources) + list(APPEND ${_cxx_sources} ${arg}) + endif() + endif() + endforeach() +endmacro() + +function(SYCL_BUILD_SHARED_LIBRARY shared_flag) + set(cmake_args ${ARGN}) + # If SHARED, MODULE, or STATIC aren't already in the list of arguments, then + # add SHARED or STATIC based on the value of BUILD_SHARED_LIBS. + list(FIND cmake_args SHARED _sycl_found_SHARED) + list(FIND cmake_args MODULE _sycl_found_MODULE) + list(FIND cmake_args STATIC _sycl_found_STATIC) + if( _sycl_found_SHARED GREATER -1 OR + _sycl_found_MODULE GREATER -1 OR + _sycl_found_STATIC GREATER -1) + set(_sycl_build_shared_libs) + else() + if(BUILD_SHARED_LIBS) + set(_sycl_build_shared_libs SHARED) + else() + set(_sycl_build_shared_libs STATIC) + endif() + endif() + set(${shared_flag} ${_sycl_build_shared_libs} PARENT_SCOPE) +endfunction() + +function(SYCL_COMPUTE_BUILD_PATH path build_path) + # Only deal with CMake style paths from here on out + file(TO_CMAKE_PATH "${path}" bpath) + if(IS_ABSOLUTE "${bpath}") + # Absolute paths are generally unnessary, especially if something like + # file(GLOB_RECURSE) is used to pick up the files. + + string(FIND "${bpath}" "${CMAKE_CURRENT_BINARY_DIR}" _binary_dir_pos) + if(_binary_dir_pos EQUAL 0) + file(RELATIVE_PATH bpath "${CMAKE_CURRENT_BINARY_DIR}" "${bpath}") + else() + file(RELATIVE_PATH bpath "${CMAKE_CURRENT_SOURCE_DIR}" "${bpath}") + endif() + endif() + + # This recipe is from cmLocalGenerator::CreateSafeUniqueObjectFileName in the + # CMake source. + + # Remove leading / + string(REGEX REPLACE "^[/]+" "" bpath "${bpath}") + # Avoid absolute paths by removing ':' + string(REPLACE ":" "_" bpath "${bpath}") + # Avoid relative paths that go up the tree + string(REPLACE "../" "__/" bpath "${bpath}") + # Avoid spaces + string(REPLACE " " "_" bpath "${bpath}") + + # Strip off the filename. I wait until here to do it, since removin the + # basename can make a path that looked like path/../basename turn into + # path/.. (notice the trailing slash). + get_filename_component(bpath "${bpath}" PATH) + + set(${build_path} "${bpath}" PARENT_SCOPE) + #message("${build_path} = ${bpath}") +endfunction() + +macro(SYCL_WRAP_SRCS sycl_target generated_files) + # Optional arguments + set(SYCL_flags "") + set(generated_extension ${CMAKE_${SYCL_C_OR_CXX}_OUTPUT_EXTENSION}) + + set(SYCL_include_dirs "${SYCL_INCLUDE_DIR}") + list(APPEND SYCL_include_dirs "$") + + set(SYCL_compile_definitions "$") + + SYCL_GET_SOURCES_AND_OPTIONS( + _sycl_sources + _cxx_sources + _cmake_options + ${ARGN}) + + set(_SYCL_build_shared_libs FALSE) + list(FIND _cmake_options SHARED _SYCL_found_SHARED) + list(FIND _cmake_options MODULE _SYCL_found_MODULE) + if(_SYCL_found_SHARED GREATER -1 OR _SYCL_found_MODULE GREATER -1) + set(_SYCL_build_shared_libs TRUE) + endif() + # STATIC + list(FIND _cmake_options STATIC _SYCL_found_STATIC) + if(_SYCL_found_STATIC GREATER -1) + set(_SYCL_build_shared_libs FALSE) + endif() + + if(_SYCL_build_shared_libs) + # If we are setting up code for a shared library, then we need to add extra flags for + # compiling objects for shared libraries. + set(SYCL_HOST_SHARED_FLAGS ${CMAKE_SHARED_LIBRARY_${SYCL_C_OR_CXX}_FLAGS}) + else() + set(SYCL_HOST_SHARED_FLAGS) + endif() + + set(_sycl_c_or_cxx_flags ${CMAKE_${SYCL_C_OR_CXX}_FLAGS}) + set(_sycl_host_flags "set(CMAKE_HOST_FLAGS ${_sycl_c_or_cxx_flags} ${SYCL_HOST_SHARED_FLAGS} ${SYCL_HOST_FLAGS})") + set(SYCL_host_flags ${_sycl_host_flags}) + + # Reset the output variable + set(_SYCL_wrap_generated_files "") + foreach(file ${_sycl_sources}) + get_source_file_property(_is_header ${file} HEADER_FILE_ONLY) + # SYCL kernels are in .cpp file + if((${file} MATCHES "\\.cpp$") AND NOT _is_header) + + # Determine output directory + SYCL_COMPUTE_BUILD_PATH("${file}" SYCL_build_path) + set(SYCL_compile_intermediate_directory "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${sycl_target}.dir/${SYCL_build_path}") + set(SYCL_compile_output_dir "${SYCL_compile_intermediate_directory}") + + get_filename_component( basename ${file} NAME ) + set(generated_file_path "${SYCL_compile_output_dir}/${CMAKE_CFG_INTDIR}") + set(generated_file_basename "${sycl_target}_gen_${basename}${generated_extension}") + set(generated_file "${generated_file_path}/${generated_file_basename}") + set(SYCL_generated_dependency_file "${SYCL_compile_intermediate_directory}/${generated_file_basename}.SYCL-depend") # generate by compiler options -M -MF + set(cmake_dependency_file "${SYCL_compile_intermediate_directory}/${generated_file_basename}.depend") # parse and convert SYCL_generated_dependency_file(compiler format) to cmake format + set(custom_target_script_pregen "${SYCL_compile_intermediate_directory}/${generated_file_basename}.cmake.pre-gen") + set(custom_target_script "${SYCL_compile_intermediate_directory}/${generated_file_basename}$<$>:.$>.cmake") + + set_source_files_properties("${generated_file}" + PROPERTIES + EXTERNAL_OBJECT true # This is an object file not to be compiled, but only be linked. + ) + + # Don't add CMAKE_CURRENT_SOURCE_DIR if the path is already an absolute path. + get_filename_component(file_path "${file}" PATH) + if(IS_ABSOLUTE "${file_path}") + set(source_file "${file}") + else() + set(source_file "${CMAKE_CURRENT_SOURCE_DIR}/${file}") + endif() + + list(APPEND ${sycl_target}_INTERMEDIATE_LINK_OBJECTS "${generated_file}") + + SYCL_INCLUDE_DEPENDENCIES(${cmake_dependency_file}) + + set(SYCL_build_type "Device") + + # Configure the build script + configure_file("${SYCL_run_sycl}" "${custom_target_script_pregen}" @ONLY) + file(GENERATE + OUTPUT "${custom_target_script}" + INPUT "${custom_target_script_pregen}" + ) + + set(main_dep MAIN_DEPENDENCY ${source_file}) + + if(SYCL_VERBOSE_BUILD) + set(verbose_output ON) + elseif(CMAKE_GENERATOR MATCHES "Makefiles") + set(verbose_output "$(VERBOSE)") + # This condition lets us also turn on verbose output when someone + # specifies CMAKE_VERBOSE_MAKEFILE, even if the generator isn't + # the Makefiles generator (this is important for us, Ninja users.) + elseif(CMAKE_VERBOSE_MAKEFILE) + set(verbose_output ON) + else() + set(verbose_output OFF) + endif() + + set(SYCL_build_comment_string "Building SYCL (${SYCL_build_type}) object ${generated_file_basename}") + + # Build the generated file and dependency file ########################## + add_custom_command( + OUTPUT ${generated_file} + # These output files depend on the source_file and the contents of cmake_dependency_file + ${main_dep} + DEPENDS ${SYCL_DEPEND} + DEPENDS ${SYCL_EXTERNAL_DEPEND} + DEPENDS ${custom_target_script} + # Make sure the output directory exists before trying to write to it. + COMMAND ${CMAKE_COMMAND} -E make_directory "${generated_file_path}" + COMMAND ${CMAKE_COMMAND} ARGS + -D verbose:BOOL=${verbose_output} + -D "generated_file:STRING=${generated_file}" + -P "${custom_target_script}" + WORKING_DIRECTORY "${SYCL_compile_intermediate_directory}" + COMMENT "${SYCL_build_comment_string}" + ) + + # Make sure the build system knows the file is generated. + set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) + + list(APPEND _SYCL_wrap_generated_files ${generated_file}) + + # Add the other files that we want cmake to clean on a cleanup ########## + list(APPEND SYCL_ADDITIONAL_CLEAN_FILES "${cmake_dependency_file}") + list(REMOVE_DUPLICATES SYCL_ADDITIONAL_CLEAN_FILES) + set(SYCL_ADDITIONAL_CLEAN_FILES ${SYCL_ADDITIONAL_CLEAN_FILES} CACHE INTERNAL "List of intermediate files that are part of the SYCL dependency scanning.") + endif() + endforeach() + + # Set the return parameter + set(${generated_files} ${_SYCL_wrap_generated_files}) +endmacro() + +function(_sycl_get_important_host_flags important_flags flag_string) + string(REGEX MATCHALL "-fPIC" flags "${flag_string}") + list(APPEND ${important_flags} ${flags}) + set(${important_flags} ${${important_flags}} PARENT_SCOPE) +endfunction() + +############################################################################### +# Custom Intermediate Link + +# Compute the filename to be used by SYCL_LINK_DEVICE_OBJECTS +function(SYCL_COMPUTE_DEVICE_OBJECT_FILE_NAME output_file_var sycl_target) + set(generated_extension ${CMAKE_${SYCL_C_OR_CXX}_OUTPUT_EXTENSION}) + set(output_file "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${sycl_target}.dir/${CMAKE_CFG_INTDIR}/${sycl_target}_sycl_device_obj${generated_extension}") + set(${output_file_var} "${output_file}" PARENT_SCOPE) +endfunction() + +macro(SYCL_LINK_DEVICE_OBJECTS output_file sycl_target) + set(object_files) + list(APPEND object_files ${ARGN}) + + if(object_files) + + set_source_files_properties("${output_file}" + PROPERTIES + EXTERNAL_OBJECT TRUE # This is an object file not to be compiled, but only + # be linked. + GENERATED TRUE # This file is generated during the build + ) + + set(SYCL_device_link_flags) + set(important_host_flags) + _sycl_get_important_host_flags(important_host_flags "${SYCL_HOST_FLAGS}") + set(SYCL_device_link_flags + ${link_type_flag} + ${important_host_flags} + ${SYCL_FLAGS} + ${SYCL_DEVICE_LINK_FLAGS}) + + file(RELATIVE_PATH output_file_relative_path "${CMAKE_BINARY_DIR}" "${output_file}") + + if(SYCL_VERBOSE_BUILD) + set(verbose_output ON) + elseif(CMAKE_GENERATOR MATCHES "Makefiles") + set(verbose_output "$(VERBOSE)") + # This condition lets us also turn on verbose output when someone + # specifies CMAKE_VERBOSE_MAKEFILE, even if the generator isn't + # the Makefiles generator (this is important for us, Ninja users.) + elseif(CMAKE_VERBOSE_MAKEFILE) + set(verbose_output ON) + else() + set(verbose_output OFF) + endif() + + # Build the generated file and dependency file ########################## + add_custom_command( + OUTPUT ${output_file} + DEPENDS ${object_files} + COMMAND ${SYCL_EXECUTABLE} + ${SYCL_device_link_flags} + -fsycl-link ${object_files} + -Xs ${SYCL_OFFLINE_COMPILER_FLAGS} + -o ${output_file} + COMMENT "Building SYCL device link file ${output_file_relative_path}" + ) + endif() +endmacro() + +############################################################################### +# ADD LIBRARY +macro(SYCL_ADD_LIBRARY sycl_target) + + if(SYCL_HOST_COMPILATION_CXX) + set(SYCL_C_OR_CXX CXX) + else() + set(SYCL_C_OR_CXX C) + endif() + + # Separate the sources from the options + SYCL_GET_SOURCES_AND_OPTIONS( + _sycl_sources + _cxx_sources + _cmake_options + ${ARGN}) + + SYCL_BUILD_SHARED_LIBRARY(_sycl_shared_flag ${ARGN}) + + if(_sycl_sources) + # Compile sycl sources + SYCL_WRAP_SRCS( + ${sycl_target} + ${sycl_target}_sycl_objects + ${_sycl_shared_flag} + ${ARGN}) + + # Compute the file name of the intermedate link file used for separable + # compilation. + SYCL_COMPUTE_DEVICE_OBJECT_FILE_NAME(device_object ${sycl_target}) + + # Add a custom device linkage command to produce a host relocatable object + # containing device object module. + SYCL_LINK_DEVICE_OBJECTS( + ${device_object} + ${sycl_target} + ${${sycl_target}_sycl_objects}) + + add_library( + ${sycl_target} + ${_cmake_options} + ${_cxx_sources} + ${${sycl_target}_sycl_objects} + ${device_object}) + else() + add_library( + ${sycl_target} + ${_cmake_options} + ${_cxx_sources}) + endif() + + target_link_libraries( + ${sycl_target} + ${SYCL_LINK_LIBRARIES_KEYWORD} + ${SYCL_LIBRARY}) + + set_target_properties(${sycl_target} + PROPERTIES + LINKER_LANGUAGE ${SYCL_C_OR_CXX}) + +endmacro() + +############################################################################### +# ADD EXECUTABLE +macro(SYCL_ADD_EXECUTABLE sycl_target) + + if(SYCL_HOST_COMPILATION_CXX) + set(SYCL_C_OR_CXX CXX) + else() + set(SYCL_C_OR_CXX C) + endif() + + # Separate the sources from the options + SYCL_GET_SOURCES_AND_OPTIONS( + _sycl_sources + _cxx_sources + _cmake_options + ${ARGN}) + + if(_sycl_sources) + # Compile sycl sources + SYCL_WRAP_SRCS( + ${sycl_target} + ${sycl_target}_sycl_objects + ${ARGN}) + + # Compute the file name of the intermedate link file used for separable + # compilation. + SYCL_COMPUTE_DEVICE_OBJECT_FILE_NAME(device_object ${sycl_target}) + + # Add a custom device linkage command to produce a host relocatable object + # containing device object module. + SYCL_LINK_DEVICE_OBJECTS( + ${device_object} + ${sycl_target} + ${${sycl_target}_sycl_objects}) + + add_executable( + ${sycl_target} + ${_cmake_options} + ${_cxx_sources} + ${${sycl_target}_sycl_objects} + ${device_object}) + else() + add_executable( + ${sycl_target} + ${_cmake_options} + ${_cxx_sources}) + endif() + + target_link_libraries( + ${sycl_target} + ${SYCL_LINK_LIBRARIES_KEYWORD} + ${SYCL_LIBRARY}) + + set_target_properties(${sycl_target} + PROPERTIES + LINKER_LANGUAGE ${SYCL_C_OR_CXX}) + +endmacro() + +set(SYCL_FOUND True) diff --git a/cmake/Modules/FindSYCL/make2cmake.cmake b/cmake/Modules/FindSYCL/make2cmake.cmake new file mode 100644 index 000000000..eda9227f6 --- /dev/null +++ b/cmake/Modules/FindSYCL/make2cmake.cmake @@ -0,0 +1,58 @@ +####################################################################### +# This converts a file written in makefile syntax into one that can be included +# by CMake. + +# Input variables +# +# verbose:BOOL=<> OFF: Be as quiet as possible (default) +# ON : Extra output +# +# input_file:FILEPATH=<> Path to dependency file in makefile format +# +# output_file:FILEPATH=<> Path to file with dependencies in CMake readable variable +# + +file(READ ${input_file} depend_text) + +if(NOT "${depend_text}" STREQUAL "") + + string(REPLACE "\\ " " " depend_text ${depend_text}) + string(REGEX REPLACE "^.* : " "" depend_text ${depend_text}) + string(REGEX REPLACE "[ \\\\]*\n" ";" depend_text ${depend_text}) + + set(dependency_list "") + + foreach(file ${depend_text}) + + string(REGEX REPLACE "^ +" "" file ${file}) + + if(NOT EXISTS "${file}") + if(EXISTS "/${file}") + set(file "/${file}") + else() + if(verbose) + message(WARNING " Removing non-existent dependency file: ${file}") + endif() + set(file "") + endif() + endif() + + if(file AND NOT IS_DIRECTORY "${file}") + get_filename_component(file_absolute "${file}" ABSOLUTE) + list(APPEND dependency_list "${file_absolute}") + endif() + + endforeach() + +else() + # message("FOUND NO DEPENDS") +endif() + +list(REMOVE_DUPLICATES dependency_list) +list(SORT dependency_list) + +foreach(file ${dependency_list}) + string(APPEND sycl_depend " \"${file}\"\n") +endforeach() + +file(WRITE ${output_file} "# Generated by: make2cmake.cmake\nSET(SYCL_DEPEND\n ${sycl_depend})\n\n") diff --git a/cmake/Modules/FindSYCL/run_sycl.cmake b/cmake/Modules/FindSYCL/run_sycl.cmake new file mode 100644 index 000000000..f56d49768 --- /dev/null +++ b/cmake/Modules/FindSYCL/run_sycl.cmake @@ -0,0 +1,189 @@ +########################################################################## +# This file runs the SYCL compiler commands to produce the desired output file +# along with the dependency file needed by CMake to compute dependencies. +# In addition the file checks the output of each command and if the command fails +# it deletes the output files. + +# Input variables +# +# verbose:BOOL=<> OFF: Be as quiet as possible (default) +# ON : Describe each step +# +# generated_file:STRING=<> File to generate. This argument must be passed in. + +cmake_policy(PUSH) +cmake_policy(SET CMP0007 NEW) +cmake_policy(SET CMP0010 NEW) +if(NOT generated_file) + message(FATAL_ERROR "You must specify generated_file on the command line") +endif() + +set(CMAKE_COMMAND "@CMAKE_COMMAND@") # path +set(source_file "@source_file@") # path +set(SYCL_generated_dependency_file "@SYCL_generated_dependency_file@") # path +set(cmake_dependency_file "@cmake_dependency_file@") # path +set(SYCL_make2cmake "@SYCL_make2cmake@") # path +set(SYCL_host_compiler "@SYCL_HOST_COMPILER@") # path +set(generated_file_path "@generated_file_path@") # path +set(generated_file_internal "@generated_file@") # path +set(SYCL_executable "@SYCL_EXECUTABLE@") # path +set(SYCL_flags @SYCL_FLAGS@) # list +set(SYCL_include_dirs [==[@SYCL_include_dirs@]==]) # list +set(SYCL_compile_definitions [==[@SYCL_compile_definitions@]==]) # list + +list(REMOVE_DUPLICATES SYCL_INCLUDE_DIRS) + +set(SYCL_host_compiler_flags "-fsycl-host-compiler-options=") +set(SYCL_include_args) + +foreach(dir ${SYCL_include_dirs}) + # Args with spaces need quotes around them to get them to be parsed as a single argument. + if(dir MATCHES " ") + list(APPEND SYCL_include_args "-I\"${dir}\"") + string(APPEND SYCL_host_compiler_flags "-I\"${dir}\" ") + else() + list(APPEND SYCL_include_args -I${dir}) + string(APPEND SYCL_host_compiler_flags "-I${dir} ") + endif() +endforeach() + +# Clean up list of compile definitions, add -D flags, and append to SYCL_flags +list(REMOVE_DUPLICATES SYCL_compile_definitions) +foreach(def ${SYCL_compile_definitions}) + list(APPEND SYCL_flags "-D${def}") +endforeach() + +# Choose host flags in FindSYCL.cmake +@SYCL_host_flags@ + +# Adding permissive flag for MSVC build to overcome ambiguous symbol error. +if(WIN32) + string(APPEND SYCL_host_compiler_flags "/permissive- ") +endif() + + +list(REMOVE_DUPLICATES CMAKE_HOST_FLAGS) +foreach(flag ${CMAKE_HOST_FLAGS}) + # Extra quotes are added around each flag to help SYCL parse out flags with spaces. + string(APPEND SYCL_host_compiler_flags "${flag} ") +endforeach() +foreach(def ${SYCL_compile_definitions}) + string(APPEND SYCL_host_compiler_flags "-D${def} ") +endforeach() + +# string(APPEND SYCL_host_compiler_flags "\"") +set(SYCL_host_compiler "-fsycl-host-compiler=${SYCL_host_compiler}") + +# SYCL_execute_process - Executes a command with optional command echo and status message. +# +# status - Status message to print if verbose is true +# command - COMMAND argument from the usual execute_process argument structure +# ARGN - Remaining arguments are the command with arguments +# +# SYCL_result - return value from running the command +# +# Make this a macro instead of a function, so that things like RESULT_VARIABLE +# and other return variables are present after executing the process. +macro(SYCL_execute_process status command) + set(_command ${command}) + if(NOT "x${_command}" STREQUAL "xCOMMAND") + message(FATAL_ERROR "Malformed call to SYCL_execute_process. Missing COMMAND as second argument. (command = ${command})") + endif() + if(verbose) + execute_process(COMMAND "${CMAKE_COMMAND}" -E echo -- ${status}) + # Now we need to build up our command string. We are accounting for quotes + # and spaces, anything else is left up to the user to fix if they want to + # copy and paste a runnable command line. + set(SYCL_execute_process_string) + foreach(arg ${ARGN}) + # If there are quotes, excape them, so they come through. + string(REPLACE "\"" "\\\"" arg ${arg}) + # Args with spaces need quotes around them to get them to be parsed as a single argument. + if(arg MATCHES " ") + list(APPEND SYCL_execute_process_string "\"${arg}\"") + else() + list(APPEND SYCL_execute_process_string ${arg}) + endif() + endforeach() + # Echo the command + execute_process(COMMAND ${CMAKE_COMMAND} -E echo ${SYCL_execute_process_string}) + endif() + # Run the command + execute_process(COMMAND ${ARGN} RESULT_VARIABLE SYCL_result ) +endmacro() + +# Delete the target file +SYCL_execute_process( + "Removing ${generated_file}" + COMMAND "${CMAKE_COMMAND}" -E remove "${generated_file}" + ) + +# Generate the code +if(WIN32) + set(SYCL_dependency_file_args /clang:-MD /clang:-MF /clang:${SYCL_generated_dependency_file}) +else() + set(SYCL_dependency_file_args -MD -MF "${SYCL_generated_dependency_file}") +endif() +SYCL_execute_process( + "Generating ${generated_file}" + COMMAND "${SYCL_executable}" + ${SYCL_dependency_file_args} + -c + "${source_file}" + -o "${generated_file}" + ${SYCL_include_args} + ${SYCL_host_compiler} + ${SYCL_host_compiler_flags} + ${SYCL_flags} + ) + +if(SYCL_result) + SYCL_execute_process( + "Removing ${generated_file}" + COMMAND "${CMAKE_COMMAND}" -E remove "${generated_file}" + ) + message(FATAL_ERROR "Error generating file ${generated_file}") +endif() + +# Parse *.d file to retrieve included headers. These headers are dependencies +# of custom compilation command. Inform cmake to scan these files and +# retrigger compilation if anything change in these headers. +SYCL_execute_process( + "Generating temporary cmake readable file: ${cmake_dependency_file}.tmp" + COMMAND "${CMAKE_COMMAND}" + -D "input_file:FILEPATH=${SYCL_generated_dependency_file}" + -D "output_file:FILEPATH=${cmake_dependency_file}.tmp" + -D "verbose=${verbose}" + -P "${SYCL_make2cmake}" + ) + +if(SYCL_result) + message(FATAL_ERROR "Error generating ${generated_file}") +endif() + +# Update dependencies list. When we remove some header in .cpp, then the +# header should be removed from dependencies list. Or unnecessary re-compilation +# will be triggered, when the header changes. +SYCL_execute_process( + "Copy if different ${cmake_dependency_file}.tmp to ${cmake_dependency_file}" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${cmake_dependency_file}.tmp" "${cmake_dependency_file}" + ) + +if(SYCL_result) + message(FATAL_ERROR "Error generating ${generated_file}") +endif() + +SYCL_execute_process( + "Removing ${cmake_dependency_file}.tmp and ${SYCL_generated_dependency_file}" + COMMAND "${CMAKE_COMMAND}" -E remove "${cmake_dependency_file}.tmp" "${SYCL_generated_dependency_file}" + ) + +if(SYCL_result) + message(FATAL_ERROR "Error generating ${generated_file}") +endif() + +if(verbose) + message("Generated ${generated_file} successfully.") +endif() + +cmake_policy(POP) diff --git a/cmake/Modules/FindSYCLToolkit.cmake b/cmake/Modules/FindSYCLToolkit.cmake new file mode 100644 index 000000000..14aa7b8e7 --- /dev/null +++ b/cmake/Modules/FindSYCLToolkit.cmake @@ -0,0 +1,385 @@ +#[=======================================================================[.rst: +SYCLConfig +------- + +Library to verify SYCL compatability of CMAKE_CXX_COMPILER +and passes relevant compiler flags. + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``SYCLTOOLKIT_FOUND`` + True if the system has the SYCL library. +``SYCL_COMPILER`` + SYCL compiler executable. +``SYCL_INCLUDE_DIR`` + Include directories needed to use SYCL. +``SYCL_LIBRARY_DIR`` + Libaray directories needed to use SYCL. +``SYCL_FLAGS`` + SYCL specific flags for the compiler. +``SYCL_LANGUAGE_VERSION`` + The SYCL language spec version by Compiler. + +#]=======================================================================] + +include(FindPackageHandleStandardArgs) + +set(SYCL_ROOT "") +if(DEFINED ENV{SYCL_ROOT}) + set(SYCL_ROOT $ENV{SYCL_ROOT}) +elseif(DEFINED ENV{CMPLR_ROOT}) + set(SYCL_ROOT $ENV{CMPLR_ROOT}) +else() + if(CMAKE_SYSTEM_NAME MATCHES "Linux") + set(SYCL_ROOT "/opt/intel/oneapi/compiler/latest") + elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") + set(SYCL_ROOT "C:/Program Files (x86)/Intel/oneAPI/compiler/latest") + endif() + if(NOT EXISTS ${SYCL_ROOT}) + set(SYCL_ROOT "") + endif() +endif() + +string(COMPARE EQUAL "${SYCL_ROOT}" "" nosyclfound) +if(nosyclfound) + set(SYCL_FOUND False) + set(SYCL_REASON_FAILURE "SYCL library not set!!") + set(SYCL_NOT_FOUND_MESSAGE "${SYCL_REASON_FAILURE}") + return() +endif() + +# Find SYCL compiler executable. +find_program( + SYCL_COMPILER + NAMES icx + PATHS "${SYCL_ROOT}" + PATH_SUFFIXES bin bin64 + NO_DEFAULT_PATH + ) + +function(parse_sycl_compiler_version version_number) + # Execute the SYCL compiler with the --version flag to match the version string. + execute_process(COMMAND ${SYCL_COMPILER} --version OUTPUT_VARIABLE SYCL_VERSION_STRING) + string(REGEX REPLACE "Intel\\(R\\) (.*) Compiler ([0-9]+\\.[0-9]+\\.[0-9]+) (.*)" "\\2" + SYCL_VERSION_STRING_MATCH ${SYCL_VERSION_STRING}) + string(REPLACE "." ";" SYCL_VERSION_LIST ${SYCL_VERSION_STRING_MATCH}) + # Split the version number list into major, minor, and patch components. + list(GET SYCL_VERSION_LIST 0 VERSION_MAJOR) + list(GET SYCL_VERSION_LIST 1 VERSION_MINOR) + list(GET SYCL_VERSION_LIST 2 VERSION_PATCH) + # Calculate the version number in the format XXXXYYZZ, using the formula (major * 10000 + minor * 100 + patch). + math(EXPR VERSION_NUMBER_MATCH "${VERSION_MAJOR} * 10000 + ${VERSION_MINOR} * 100 + ${VERSION_PATCH}") + set(${version_number} "${VERSION_NUMBER_MATCH}" PARENT_SCOPE) +endfunction() + +if(SYCL_COMPILER) + parse_sycl_compiler_version(SYCL_COMPILER_VERSION) +endif() + +if(NOT SYCL_COMPILER_VERSION) + set(SYCL_FOUND False) + set(SYCL_REASON_FAILURE "Cannot parse sycl compiler version to get SYCL_COMPILER_VERSION!") + set(SYCL_NOT_FOUND_MESSAGE "${SYCL_REASON_FAILURE}") + return() +endif() + +# Find include path from binary. +find_file( + SYCL_INCLUDE_DIR + NAMES include + HINTS ${SYCL_ROOT} + NO_DEFAULT_PATH + ) + +# Find include/sycl path from include path. +find_file( + SYCL_INCLUDE_SYCL_DIR + NAMES sycl + HINTS ${SYCL_ROOT}/include/ + NO_DEFAULT_PATH + ) + +# Due to the unrecognized compilation option `-fsycl` in other compiler. +list(APPEND SYCL_INCLUDE_DIR ${SYCL_INCLUDE_SYCL_DIR}) + +# Find library directory from binary. +find_file( + SYCL_LIBRARY_DIR + NAMES lib lib64 + HINTS ${SYCL_ROOT} + NO_DEFAULT_PATH + ) + +set(COMPATIBLE_SYCL_TOOLKIT_VERSION 20249999) +# By default, we use libsycl.so on Linux and sycl.lib on Windows as the SYCL library name. +if (SYCL_COMPILER_VERSION VERSION_LESS_EQUAL COMPATIBLE_SYCL_TOOLKIT_VERSION) + # Don't use if(LINUX) here since this requires cmake>=3.25 and file is installed + # and used by other projects. + # See: https://cmake.org/cmake/help/v3.25/variable/LINUX.html + if(CMAKE_SYSTEM_NAME MATCHES "Linux") + set(sycl_lib_suffix "-preview") + elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") + # On Windows, the SYCL library is named sycl7.lib until COMPATIBLE_SYCL_TOOLKIT_VERSION. + # sycl.lib is supported in the later version. + set(sycl_lib_suffix "7") + endif() +endif() + +# Find SYCL library fullname. +find_library( + SYCL_LIBRARY + NAMES "sycl${sycl_lib_suffix}" + HINTS ${SYCL_LIBRARY_DIR} + NO_DEFAULT_PATH +) + +# Find OpenCL library fullname, which is a dependency of oneDNN. +find_library( + OCL_LIBRARY + NAMES OpenCL + HINTS ${SYCL_LIBRARY_DIR} + NO_DEFAULT_PATH +) + +if((NOT SYCL_LIBRARY) OR (NOT OCL_LIBRARY)) + set(SYCL_FOUND False) + set(SYCL_REASON_FAILURE "SYCL library is incomplete!!") + set(SYCL_NOT_FOUND_MESSAGE "${SYCL_REASON_FAILURE}") + return() +endif() + +find_package_handle_standard_args( + SYCL + FOUND_VAR SYCL_FOUND + REQUIRED_VARS SYCL_INCLUDE_DIR SYCL_LIBRARY_DIR SYCL_LIBRARY + REASON_FAILURE_MESSAGE "${SYCL_REASON_FAILURE}" + VERSION_VAR SYCL_COMPILER_VERSION + ) + +if(NOT SYCL_FOUND) + set(SYCLTOOLKIT_FOUND FALSE) + return() +endif() + +if(SYCLTOOLKIT_FOUND) + return() +endif() + +set(SYCLTOOLKIT_FOUND TRUE) + +include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) + +if(WIN32) + set(SYCL_EXECUTABLE_NAME icx) +else() + set(SYCL_EXECUTABLE_NAME icpx) +endif() + +if(NOT SYCL_ROOT) + execute_process( + COMMAND which ${SYCL_EXECUTABLE_NAME} + OUTPUT_VARIABLE SYCL_CMPLR_FULL_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(NOT EXISTS "${SYCL_CMPLR_FULL_PATH}") + message(WARNING "Cannot find ENV{CMPLR_ROOT} or icpx, please setup SYCL compiler Tool kit enviroment before building!!") + return() + endif() + + get_filename_component(SYCL_BIN_DIR "${SYCL_CMPLR_FULL_PATH}" DIRECTORY) + set(SYCL_ROOT ${SYCL_BIN_DIR}/..) +endif() + +find_program( + SYCL_COMPILER + NAMES ${SYCL_EXECUTABLE_NAME} + PATHS "${SYCL_ROOT}" + PATH_SUFFIXES bin bin64 + NO_DEFAULT_PATH + ) + +string(COMPARE EQUAL "${SYCL_COMPILER}" "" nocmplr) +if(nocmplr) + set(SYCLTOOLKIT_FOUND False) + set(SYCL_REASON_FAILURE "SYCL: CMAKE_CXX_COMPILER not set!!") + set(SYCL_NOT_FOUND_MESSAGE "${SYCL_REASON_FAILURE}") +endif() + +# Function to write a test case to verify SYCL features. + +function(SYCL_CMPLR_TEST_WRITE src macro_name) + + set(cpp_macro_if "#if") + set(cpp_macro_endif "#endif") + + set(SYCL_CMPLR_TEST_CONTENT "") + string(APPEND SYCL_CMPLR_TEST_CONTENT "#include \nusing namespace std;\n") + string(APPEND SYCL_CMPLR_TEST_CONTENT "int main(){\n") + + # Feature tests goes here + + string(APPEND SYCL_CMPLR_TEST_CONTENT "${cpp_macro_if} defined(${macro_name})\n") + string(APPEND SYCL_CMPLR_TEST_CONTENT "cout << \"${macro_name}=\"<<${macro_name}<\nint main() { std::cout << \"Checking compiler options ...\" << std::endl; return 0; }\n") + execute_process( + COMMAND ${SYCL_COMPILER} -fsycl ${TEST_SRC_FILE} -o ${TEST_EXE_FILE} ${FLAG} + WORKING_DIRECTORY ${TEMP_DIR} + OUTPUT_VARIABLE output + ERROR_VARIABLE output + RESULT_VARIABLE result + TIMEOUT 60 + ) + if(result EQUAL 0) + set(${VARIABLE_NAME} TRUE PARENT_SCOPE) + else() + set(${VARIABLE_NAME} FALSE PARENT_SCOPE) + endif() + file(REMOVE_RECURSE ${TEMP_DIR}) +endfunction() + +# Support GCC on Linux and MSVC on Windows at the moment. +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # # -- Host flags (SYCL_CXX_FLAGS) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + list(APPEND SYCL_HOST_FLAGS /std:c++17) + list(APPEND SYCL_HOST_FLAGS /MD) + list(APPEND SYCL_HOST_FLAGS /EHsc) # exception handling + # SYCL headers warnings + list(APPEND SYCL_HOST_FLAGS /wd4996) # allow usage of deprecated functions + list(APPEND SYCL_HOST_FLAGS /wd4018) # allow signed and unsigned comparison + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND SYCL_HOST_FLAGS -fPIC) + list(APPEND SYCL_HOST_FLAGS -std=c++17) + # SYCL headers warnings + list(APPEND SYCL_HOST_FLAGS -Wno-deprecated-declarations) + list(APPEND SYCL_HOST_FLAGS -Wno-deprecated) + list(APPEND SYCL_HOST_FLAGS -Wno-attributes) + list(APPEND SYCL_HOST_FLAGS -Wno-sign-compare) + endif() + + if(CMAKE_BUILD_TYPE MATCHES Debug) + list(APPEND SYCL_HOST_FLAGS -g) + list(APPEND SYCL_HOST_FLAGS -O0) + endif(CMAKE_BUILD_TYPE MATCHES Debug) + + if(USE_PER_OPERATOR_HEADERS) + list(APPEND SYCL_HOST_FLAGS -DAT_PER_OPERATOR_HEADERS) + endif() + list(APPEND SYCL_HOST_FLAGS -D__INTEL_LLVM_COMPILER_VERSION=${__INTEL_LLVM_COMPILER}) + # -- Kernel flags (SYCL_KERNEL_OPTIONS) + # The fast-math will be enabled by default in SYCL compiler. + # Refer to [https://clang.llvm.org/docs/UsersManual.html#cmdoption-fno-fast-math] + # 1. We enable below flags here to be warn about NaN and Infinity, + # which will be hidden by fast-math by default. + # 2. The associative-math in fast-math allows floating point + # operations to be reassociated, which will lead to non-deterministic + # results compared with CUDA backend. + # 3. The approx-func allows certain math function calls (such as log, sqrt, pow, etc) + # to be replaced with an approximately equivalent set of instructions or + # alternative math function calls, which have great errors. + # + # PSEUDO of separate compilation with DPCPP compiler. + # 1. Kernel source compilation: + # icpx -fsycl -fsycl-target=${SYCL_TARGETS_OPTION} ${SYCL_FLAGS} -fsycl-host-compiler=gcc -fsycl-host-compiler-options='${CMAKE_HOST_FLAGS}' kernel.cpp -o kernel.o + # 2. Device code linkage: + # icpx -fsycl -fsycl-target=${SYCL_TARGETS_OPTION} -fsycl-link ${SYCL_DEVICE_LINK_FLAGS} -Xs '${SYCL_OFFLINE_COMPILER_FLAGS}' kernel.o -o device-code.o + # 3. Host only source compilation: + # gcc ${CMAKE_HOST_FLAGS} host.cpp -o host.o + # 4. Linkage: + # gcc -shared host.o kernel.o device-code.o -o libxxx.so + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -fno-sycl-unnamed-lambda) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -sycl-std=2020) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} /fp:strict) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -fhonor-nans) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -fhonor-infinities) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -fno-associative-math) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -fno-approx-func) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -Wno-absolute-value) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -no-ftz) + endif() + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -D__INTEL_LLVM_COMPILER_VERSION=${__INTEL_LLVM_COMPILER}) + + CHECK_SYCL_FLAG("-fsycl-fp64-conv-emu" SUPPORTS_FP64_CONV_EMU) + if(SUPPORTS_FP64_CONV_EMU) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -fsycl-fp64-conv-emu) + else() + message(WARNING "The compiler does not support the '-fsycl-fp64-conv-emu' flag, \ + will disable it. On some platforms that don't support FP64, \ + running operations with the FP64 datatype will raise a Runtime error: Required aspect fp64 is not supported on the device \ + or a Native API failed error.") + endif() + + # -- SYCL device object linkage flags + include(ProcessorCount) + ProcessorCount(proc_cnt) + if((DEFINED ENV{MAX_JOBS}) AND ("$ENV{MAX_JOBS}" LESS_EQUAL ${proc_cnt})) + set(SYCL_MAX_PARALLEL_LINK_JOBS $ENV{MAX_JOBS}) + else() + set(SYCL_MAX_PARALLEL_LINK_JOBS ${proc_cnt}) + endif() + set(SYCL_DEVICE_LINK_FLAGS ${SYCL_DEVICE_LINK_FLAGS} -fsycl-max-parallel-link-jobs=${SYCL_MAX_PARALLEL_LINK_JOBS}) + set(SYCL_DEVICE_LINK_FLAGS ${SYCL_DEVICE_LINK_FLAGS} --offload-compress) + + set(SYCL_OFFLINE_COMPILER_CG_OPTIONS "${SYCL_OFFLINE_COMPILER_CG_OPTIONS} -options -cl-poison-unsupported-fp64-kernels") + set(SYCL_OFFLINE_COMPILER_CG_OPTIONS "${SYCL_OFFLINE_COMPILER_CG_OPTIONS} -options -cl-intel-enable-auto-large-GRF-mode") + set(SYCL_OFFLINE_COMPILER_CG_OPTIONS "${SYCL_OFFLINE_COMPILER_CG_OPTIONS} -options -cl-fp32-correctly-rounded-divide-sqrt") + set(SYCL_OFFLINE_COMPILER_CG_OPTIONS "${SYCL_OFFLINE_COMPILER_CG_OPTIONS} -options -cl-intel-greater-than-4GB-buffer-required") + + + if(WIN32) + set(AOT_TARGETS "lnl-m") + else() + set(AOT_TARGETS "pvc") + endif() + if(BNB_XPU_ARCH_LIST) + set(AOT_TARGETS "${BNB_XPU_ARCH_LIST}") + endif() + if(AOT_TARGETS STREQUAL "none") + set(BNB_XPU_ARCH_LIST "" PARENT_SCOPE) + else() + set(SYCL_TARGETS_OPTION -fsycl-targets=spir64_gen,spir64) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} ${SYCL_TARGETS_OPTION}) + set(SYCL_DEVICE_LINK_FLAGS ${SYCL_DEVICE_LINK_FLAGS} ${SYCL_TARGETS_OPTION}) + set(SYCL_OFFLINE_COMPILER_AOT_OPTIONS "-device ${AOT_TARGETS}") + endif() + message(STATUS "Compile Intel GPU AOT Targets for ${AOT_TARGETS}") + + set(SYCL_FLAGS ${SYCL_FLAGS} ${SYCL_KERNEL_OPTIONS}) + + set(SYCL_OFFLINE_COMPILER_FLAGS "${SYCL_OFFLINE_COMPILER_AOT_OPTIONS}${SYCL_OFFLINE_COMPILER_CG_OPTIONS}") +else() + message("Not compiling with XPU. Currently only support GCC compiler on Linux and MSVC compiler on Windows as CXX compiler.") + return() +endif() diff --git a/tests/sycl/CMakeLists.txt b/tests/sycl/CMakeLists.txt new file mode 100644 index 000000000..08193c0cd --- /dev/null +++ b/tests/sycl/CMakeLists.txt @@ -0,0 +1,45 @@ +set(TEST_SYCL_ROOT ${PROJECT_SOURCE_DIR}/tests/sycl) +set(TEST_SYCL_SYCL_SRCS ${TEST_SYCL_ROOT}/simple_kernel.cpp) +set(TEST_SYCL_CXX_SRCS ${TEST_SYCL_ROOT}/main.cpp) + +# test building executable +sycl_add_executable( + test_sycl_build_standalone + SYCL_SOURCES ${TEST_SYCL_SYCL_SRCS} + CXX_SOURCES ${TEST_SYCL_CXX_SRCS} ${TEST_SYCL_CXX_SRCS}) + +install(TARGETS test_sycl_build_standalone DESTINATION bin) + +# test building archive static library +sycl_add_library( + sycl_simple_kernel_test + STATIC + SYCL_SOURCES ${TEST_SYCL_SYCL_SRCS}) + +add_executable( + test_sycl_build_archive + ${TEST_SYCL_CXX_SRCS}) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Windows + target_link_libraries(test_sycl_build_archive + "-WHOLEARCHIVE:\"$\"") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # Linux + target_link_libraries(test_sycl_build_archive + "-Wl,--whole-archive,\"$\" -Wl,--no-whole-archive") +endif() +add_dependencies(test_sycl_build_archive sycl_simple_kernel_test) + +# SYCL runtime library could be a transitive link library of +# ``test_sycl_build_archive``, if using, +# `` +# target_link_libraries( +# test_sycl_build_archive +# sycl_simple_kernel_test) +# `` +# Instead, we use explicit linkage option '--whole-archive', which is required +# by linkage of device object modules archived in the static library. Then +# explicit linkage configuration of SYCL runtime library is required. +target_link_libraries(test_sycl_build_archive ${SYCL_LIBRARY}) + +install(TARGETS test_sycl_build_archive DESTINATION bin) diff --git a/tests/sycl/main.cpp b/tests/sycl/main.cpp new file mode 100644 index 000000000..39f4c5388 --- /dev/null +++ b/tests/sycl/main.cpp @@ -0,0 +1,29 @@ +#include +#include "simple_kernel.hpp" + +void test_simple_kernel() { + int numel = 1024; + float a[1024]; + + // a simple sycl kernel + itoa(a, numel); + + bool success = true; + for (int i = 0; i < numel; i++) { + if (a[i] != i) { + success = false; + break; + } + } + + if (success) { + std::cout << "Pass" << std::endl; + } else { + std::cout << "Fail" << std::endl; + } +} + +int main(int argc, char* argv[]) { + test_simple_kernel(); + return 0; +} diff --git a/tests/sycl/simple_kernel.cpp b/tests/sycl/simple_kernel.cpp new file mode 100644 index 000000000..471925e2a --- /dev/null +++ b/tests/sycl/simple_kernel.cpp @@ -0,0 +1,58 @@ +#include + +class SimpleKer { + public: + SimpleKer(float* a) : a_(a) {} + void operator()(sycl::item<1> item) const { + a_[item] = item; + } + + private: + float* a_; +}; + +int enum_gpu_device(sycl::device& dev) { + std::vector root_devices; + auto platform_list = sycl::platform::get_platforms(); + for (const auto& platform : platform_list) { + if (platform.get_backend() != sycl::backend::ext_oneapi_level_zero) { + continue; + } + auto device_list = platform.get_devices(); + for (const auto& device : device_list) { + if (device.is_gpu()) { + root_devices.push_back(device); + } + } + } + + if (root_devices.empty()) { + throw std::runtime_error( + "test_sycl: simple_kernel: no GPU device found ..."); + return -1; + } + + dev = root_devices[0]; + return 0; +} + +void itoa(float* res, int numel) { + sycl::device dev; + if (enum_gpu_device(dev)) { + return; + } + sycl::queue q = sycl::queue(dev, sycl::property_list()); + + float* a = sycl::malloc_shared(numel, q); + auto cgf = [&](sycl::handler& cgh) { + cgh.parallel_for(sycl::range<1>(numel), SimpleKer(a)); + }; + + auto e = q.submit(cgf); + e.wait(); + + memcpy(res, a, numel * sizeof(float)); + sycl::free(a, q); + + return; +} diff --git a/tests/sycl/simple_kernel.hpp b/tests/sycl/simple_kernel.hpp new file mode 100644 index 000000000..0d01ec8fb --- /dev/null +++ b/tests/sycl/simple_kernel.hpp @@ -0,0 +1,6 @@ +#pragma once + +// Create an idx array on SYCL GPU device +// res - host buffer for result +// numel - length of the idx array +void itoa(float* res, int numel);