diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..6c635ef6f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +# torch-xpu-ops: XPU implementation for PyTorch ATen + +# outputs: +# +# PYTORCH_FOUND_XPU +# -- The flag to indicate whether XPU backend stacks are setup successfully or not. +# +# libtorch_xpu_ops +# -- Static archive library target + +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) +project(${TORCH_XPU_OPS_PROJ_NAME} VERSION ${CMAKE_PROJECT_VERSION}) + +set(PYTORCH_FOUND_XPU FALSE) + +if(NOT CMAKE_SYSTEM_NAME MATCHES "Linux") + message("torch-xpu-ops only supports Linux system so far. We will support more systems in future.") + return() +endif() + +set(TORCH_XPU_OPS_ROOT ${PROJECT_SOURCE_DIR}) +list(APPEND CMAKE_MODULE_PATH ${TORCH_XPU_OPS_ROOT}/cmake/Modules) + +include(${TORCH_XPU_OPS_ROOT}/cmake/SYCL.cmake) +include(${TORCH_XPU_OPS_ROOT}/cmake/BuildFlags.cmake) + +if(BUILD_TEST) + add_subdirectory(${TORCH_XPU_OPS_ROOT}/test/sycl ${CMAKE_BINARY_DIR}/test_sycl) +endif() + +set(PYTORCH_FOUND_XPU TRUE) + +message(STATUS "XPU found") diff --git a/cmake/BuildFlags.cmake b/cmake/BuildFlags.cmake new file mode 100644 index 000000000..71734e47f --- /dev/null +++ b/cmake/BuildFlags.cmake @@ -0,0 +1,41 @@ +# Setup building flags for SYCL device and host codes. + +# Support GCC only at the moment. +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # # -- Host flags (SYCL_CXX_FLAGS) + 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-attributes) + + if(CMAKE_BUILD_TYPE MATCHES Debug) + list(APPEND SYCL_HOST_FLAGS -g) + list(APPEND SYCL_HOST_FLAGS -O0) + endif(CMAKE_BUILD_TYPE MATCHES Debug) + + # -- 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. + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -fno-sycl-unnamed-lambda) + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -sycl-std=2020) + 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) + # TODO: Align with PyTorch and switch to ABI=0 eventually, after + # resolving incompatible implementation in SYCL runtime. + set(SYCL_KERNEL_OPTIONS ${SYCL_KERNEL_OPTIONS} -D_GLIBCXX_USE_CXX11_ABI=1) + set(SYCL_FLAGS ${SYCL_FLAGS} ${SYCL_KERNEL_OPTIONS}) +else() + message("Not compiling with XPU. Only support GCC compiler as CXX compiler.") + return() +endif() diff --git a/cmake/Modules/FindSYCL.cmake b/cmake/Modules/FindSYCL.cmake new file mode 100644 index 000000000..fd74de8e9 --- /dev/null +++ b/cmake/Modules/FindSYCL.cmake @@ -0,0 +1,502 @@ +#.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_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() + find_program(SYCL_EXECUTABLE + NAMES icpx + PATHS "${SYCL_PACKAGE_DIR}" + PATH_SUFFIXES bin bin64 + NO_DEFAULT_PATH + ) +endif() + +set(SYCL_LIBRARIES) +find_library(SYCL_RUNTIME_LIBRARY sycl HINTS ${SYCL_LIBRARY_DIR}) +list(APPEND SYCL_LIBRARIES ${SYCL_RUNTIME_LIBRARY}) + +# 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_DEPENDENCIES dependency_file) + # Make the output depend on the dependency file itself, which should cause the + # rule to re-run. + set(SYCL_DEPEND ${dependency_file}) + 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}) +endmacro() + +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}_generated_${basename}${generated_extension}") + set(generated_file "${generated_file_path}/${generated_file_basename}") + set(cmake_dependency_file "${SYCL_compile_intermediate_directory}/${generated_file_basename}.depend") + set(SYCL_generated_dependency_file "${SYCL_compile_intermediate_directory}/${generated_file_basename}.SYCL-depend") + 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_relative_path}") + + # 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 ${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 sycl_objects) + set(object_files) + list(APPEND object_files ${sycl_objects}) + + 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}) + + file(REAL_PATH working_directory "${output_file}") + 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} -fsycl ${SYCL_device_link_flags} -fsycl-link ${object_files} -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_LIBRARIES}) + + 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_LIBRARIES}) + + set_target_properties(${sycl_target} + PROPERTIES + LINKER_LANGUAGE ${SYCL_C_OR_CXX}) + +endmacro() + +set(SYCL_FOUND True) diff --git a/cmake/Modules/FindSYCL/run_sycl.cmake b/cmake/Modules/FindSYCL/run_sycl.cmake new file mode 100644 index 000000000..fcede5613 --- /dev/null +++ b/cmake/Modules/FindSYCL/run_sycl.cmake @@ -0,0 +1,134 @@ +########################################################################## +# 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_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}) + # Extra quotes are added around each flag to help SYCL parse out flags with spaces. + list(APPEND SYCL_include_args "-I${dir}") + string(APPEND SYCL_host_compiler_flags "-I${dir} ") +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@ + +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 +SYCL_execute_process( + "Generating ${generated_file}" + COMMAND "${SYCL_executable}" + -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}") +else() + if(verbose) + message("Generated ${generated_file} successfully.") + endif() +endif() + +cmake_policy(POP) diff --git a/cmake/Modules/FindSYCLToolkit.cmake b/cmake/Modules/FindSYCLToolkit.cmake new file mode 100644 index 000000000..f49e95979 --- /dev/null +++ b/cmake/Modules/FindSYCLToolkit.cmake @@ -0,0 +1,262 @@ +#[=======================================================================[.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. + +``SYCL::SYCL_CXX`` + Interface target for using SYCL compiler. The following properties are + defined for the target: ``INTERFACE_COMPILE_OPTIONS``, + ``INTERFACE_LINK_OPTIONS``, ``INTERFACE_INCLUDE_DIRECTORIES``, and + ``INTERFACE_LINK_DIRECTORIES`` + +#]=======================================================================] + +set(SYCLTOOLKIT_FOUND False) +include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) + +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}) +endif() +if(NOT SYCL_ROOT) + execute_process( + COMMAND which icpx + 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_file( + SYCL_COMPILER + NAMES icpx + HINTS ${SYCL_ROOT}/bin + 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() + +find_file( + SYCL_INCLUDE_DIR + NAMES include + HINTS ${SYCL_ROOT} + NO_DEFAULT_PATH + ) + +find_file( + SYCL_INCLUDE_SYCL_DIR + NAMES sycl + HINTS ${SYCL_ROOT}/include + NO_DEFAULT_PATH + ) + +list(APPEND SYCL_INCLUDE_DIR ${SYCL_INCLUDE_SYCL_DIR}) + +find_file( + SYCL_LIBRARY_DIR + NAMES lib lib64 + HINTS ${SYCL_ROOT} + NO_DEFAULT_PATH + ) + +find_library( + SYCL_LIBRARY + NAMES sycl + HINTS ${SYCL_LIBRARY_DIR} + NO_DEFAULT_PATH +) + +if((NOT SYCL_INCLUDE_DIR) OR (NOT SYCL_LIBRARY_DIR) OR (NOT SYCL_LIBRARY)) + set(SYCLTOOLKIT_FOUND False) + set(SYCL_REASON_FAILURE "SYCL sdk is incomplete!!") + set(SYCL_NOT_FOUND_MESSAGE "${SYCL_REASON_FAILURE}") + return() +endif() + +# Function to write a test case to verify SYCL features. + +function(SYCL_CMPLR_TEST_WRITE src) + + 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(SYCL_LANGUAGE_VERSION)\n") + string(APPEND SYCL_CMPLR_TEST_CONTENT "cout << \"SYCL_LANGUAGE_VERSION=\"<\" -Wl,--no-whole-archive") +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_LIBRARIES}) + +if(INSTALL_TEST) + install(TARGETS test_sycl_build_archive DESTINATION bin) +endif() diff --git a/test/sycl/main.cpp b/test/sycl/main.cpp new file mode 100644 index 000000000..39f4c5388 --- /dev/null +++ b/test/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/test/sycl/simple_kernel.cpp b/test/sycl/simple_kernel.cpp new file mode 100644 index 000000000..471925e2a --- /dev/null +++ b/test/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/test/sycl/simple_kernel.hpp b/test/sycl/simple_kernel.hpp new file mode 100644 index 000000000..0d01ec8fb --- /dev/null +++ b/test/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); diff --git a/test/sycl/test_simple_kernel.cpp b/test/sycl/test_simple_kernel.cpp new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/sycl/test_simple_kernel.cpp @@ -0,0 +1 @@ +