Skip to content

[mlir][ExecutionEngine] Add LevelZeroRuntimeWrapper. #151038

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

mshahneo
Copy link
Contributor

@mshahneo mshahneo commented Jul 28, 2025

Adds LevelZeroRuntime wrapper and tests.

Co-authored-by: Artem Kroviakov [email protected]
Co-authored-by: Nishant Patel [email protected]

Adds LevelZeroRuntime wrapper and tests.

Co-authored-by: Artem Kroviakov <[email protected]>
Co-authored-by: Nishant Patel <[email protected]>
@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-mlir-gpu

Author: Md Abdullah Shahneous Bari (mshahneo)

Changes

Adds LevelZeroRuntime wrapper and tests.


Patch is 53.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151038.diff

8 Files Affected:

  • (renamed) mlir/cmake/modules/FindLevelZeroRuntime.cmake (+60-53)
  • (modified) mlir/lib/ExecutionEngine/CMakeLists.txt (+83-34)
  • (added) mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp (+491)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-addf32-to-spirv.mlir (+56)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-addi64-to-spirv.mlir (+54)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-memcpy-addf32-to-spirv.mlir (+53)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-reluf32-to-spirv.mlir (+79)
  • (added) mlir/test/Integration/GPU/LEVELZERO/lit.local.cfg (+2)
diff --git a/mlir/cmake/modules/FindLevelZero.cmake b/mlir/cmake/modules/FindLevelZeroRuntime.cmake
similarity index 66%
rename from mlir/cmake/modules/FindLevelZero.cmake
rename to mlir/cmake/modules/FindLevelZeroRuntime.cmake
index 012187f0afc0b..b1e8e5b6387f2 100644
--- a/mlir/cmake/modules/FindLevelZero.cmake
+++ b/mlir/cmake/modules/FindLevelZeroRuntime.cmake
@@ -20,7 +20,6 @@ include(FindPackageHandleStandardArgs)
 # Search path priority
 # 1. CMake Variable LEVEL_ZERO_DIR
 # 2. Environment Variable LEVEL_ZERO_DIR
-
 if(NOT LEVEL_ZERO_DIR)
     if(DEFINED ENV{LEVEL_ZERO_DIR})
         set(LEVEL_ZERO_DIR "$ENV{LEVEL_ZERO_DIR}")
@@ -28,32 +27,32 @@ if(NOT LEVEL_ZERO_DIR)
 endif()
 
 if(LEVEL_ZERO_DIR)
-    find_path(LevelZero_INCLUDE_DIR
+    find_path(LevelZeroRuntime_INCLUDE_DIR
         NAMES level_zero/ze_api.h
         PATHS ${LEVEL_ZERO_DIR}/include
         NO_DEFAULT_PATH
     )
 
     if(LINUX)
-        find_library(LevelZero_LIBRARY
+        find_library(LevelZeroRuntime_LIBRARY
             NAMES ze_loader
             PATHS ${LEVEL_ZERO_DIR}/lib
-                  ${LEVEL_ZERO_DIR}/lib/x86_64-linux-gnu
+            ${LEVEL_ZERO_DIR}/lib/x86_64-linux-gnu
             NO_DEFAULT_PATH
         )
     else()
-        find_library(LevelZero_LIBRARY
+        find_library(LevelZeroRuntime_LIBRARY
             NAMES ze_loader
             PATHS ${LEVEL_ZERO_DIR}/lib
             NO_DEFAULT_PATH
         )
     endif()
 else()
-    find_path(LevelZero_INCLUDE_DIR
+    find_path(LevelZeroRuntime_INCLUDE_DIR
         NAMES level_zero/ze_api.h
     )
 
-    find_library(LevelZero_LIBRARY
+    find_library(LevelZeroRuntime_LIBRARY
         NAMES ze_loader
     )
 endif()
@@ -64,26 +63,33 @@ endif()
 # lists of equal lengths, with the shorter string getting zero-padded.
 function(compare_versions VERSION_STR1 VERSION_STR2 OUTPUT)
     # Convert the strings to list
-    string(REPLACE  "." ";" VL1 ${VERSION_STR1})
-    string(REPLACE  "." ";" VL2 ${VERSION_STR2})
+    string(REPLACE "." ";" VL1 ${VERSION_STR1})
+    string(REPLACE "." ";" VL2 ${VERSION_STR2})
+
     # get lengths of both lists
     list(LENGTH VL1 VL1_LEN)
     list(LENGTH VL2 VL2_LEN)
     set(LEN ${VL1_LEN})
+
     # If they differ in size pad the shorter list with 0s
     if(VL1_LEN GREATER VL2_LEN)
         math(EXPR DIFF "${VL1_LEN} - ${VL2_LEN}" OUTPUT_FORMAT DECIMAL)
+
         foreach(IDX RANGE 1 ${DIFF} 1)
             list(APPEND VL2 "0")
         endforeach()
     elseif(VL2_LEN GREATER VL2_LEN)
         math(EXPR DIFF "${VL1_LEN} - ${VL2_LEN}" OUTPUT_FORMAT DECIMAL)
+
         foreach(IDX RANGE 1 ${DIFF} 1)
             list(APPEND VL2 "0")
         endforeach()
+
         set(LEN ${VL2_LEN})
     endif()
+
     math(EXPR LEN_SUB_ONE "${LEN}-1")
+
     foreach(IDX RANGE 0 ${LEN_SUB_ONE} 1)
         list(GET VL1 ${IDX} VAL1)
         list(GET VL2 ${IDX} VAL2)
@@ -98,12 +104,10 @@ function(compare_versions VERSION_STR1 VERSION_STR2 OUTPUT)
             set(${OUTPUT} TRUE PARENT_SCOPE)
         endif()
     endforeach()
-
-    endfunction(compare_versions)
+endfunction(compare_versions)
 
 # Creates a small function to run and extract the LevelZero loader version.
 function(get_l0_loader_version)
-
     set(L0_VERSIONEER_SRC
         [====[
         #include <iostream>
@@ -142,19 +146,20 @@ function(get_l0_loader_version)
 
     # We need both the directories in the include path as ze_loader.h
     # includes "ze_api.h" and not "level_zero/ze_api.h".
-    list(APPEND INCLUDE_DIRS ${LevelZero_INCLUDE_DIR})
-    list(APPEND INCLUDE_DIRS ${LevelZero_INCLUDE_DIR}/level_zero)
+    list(APPEND INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR})
+    list(APPEND INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR}/level_zero)
     list(JOIN INCLUDE_DIRS ";" INCLUDE_DIRS_STR)
     try_run(L0_VERSIONEER_RUN L0_VERSIONEER_COMPILE
-            "${CMAKE_BINARY_DIR}"
-            "${L0_VERSIONEER_FILE}"
-            LINK_LIBRARIES ${LevelZero_LIBRARY}
-            CMAKE_FLAGS
-                "-DINCLUDE_DIRECTORIES=${INCLUDE_DIRS_STR}"
-            RUN_OUTPUT_VARIABLE L0_VERSION
+        "${CMAKE_BINARY_DIR}"
+        "${L0_VERSIONEER_FILE}"
+        LINK_LIBRARIES ${LevelZeroRuntime_LIBRARY}
+        CMAKE_FLAGS
+        "-DINCLUDE_DIRECTORIES=${INCLUDE_DIRS_STR}"
+        RUN_OUTPUT_VARIABLE L0_VERSION
     )
-    if(${L0_VERSIONEER_COMPILE} AND (DEFINED L0_VERSIONEER_RUN))
-        set(LevelZero_VERSION ${L0_VERSION} PARENT_SCOPE)
+
+    if(${L0_VERSIONEER_COMPILE} AND(DEFINED L0_VERSIONEER_RUN))
+        set(LevelZeroRuntime_VERSION ${L0_VERSION} PARENT_SCOPE)
         message(STATUS "Found Level Zero of version: ${L0_VERSION}")
     else()
         message(FATAL_ERROR
@@ -163,59 +168,61 @@ function(get_l0_loader_version)
     endif()
 endfunction(get_l0_loader_version)
 
-if(LevelZero_INCLUDE_DIR AND LevelZero_LIBRARY)
-    list(APPEND LevelZero_LIBRARIES "${LevelZero_LIBRARY}")
-    list(APPEND LevelZero_INCLUDE_DIRS ${LevelZero_INCLUDE_DIR})
+if(LevelZeroRuntime_INCLUDE_DIR AND LevelZeroRuntime_LIBRARY)
+    list(APPEND LevelZeroRuntime_LIBRARIES "${LevelZeroRuntime_LIBRARY}")
+    list(APPEND LevelZeroRuntime_INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR})
+
     if(OpenCL_FOUND)
-      list(APPEND LevelZero_INCLUDE_DIRS ${OpenCL_INCLUDE_DIRS})
+        list(APPEND LevelZeroRuntime_INCLUDE_DIRS ${OpenCL_INCLUDE_DIRS})
     endif()
 
-    cmake_path(GET LevelZero_LIBRARY PARENT_PATH LevelZero_LIBRARIES_PATH)
-    set(LevelZero_LIBRARIES_DIR ${LevelZero_LIBRARIES_PATH})
-
-    if(NOT TARGET LevelZero::LevelZero)
-      add_library(LevelZero::LevelZero INTERFACE IMPORTED)
-      set_target_properties(LevelZero::LevelZero
-        PROPERTIES INTERFACE_LINK_LIBRARIES "${LevelZero_LIBRARIES}"
-      )
-      set_target_properties(LevelZero::LevelZero
-        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LevelZero_INCLUDE_DIRS}"
-      )
+    cmake_path(GET LevelZeroRuntime_LIBRARY PARENT_PATH LevelZeroRuntime_LIBRARIES_PATH)
+    set(LevelZeroRuntime_LIBRARIES_DIR ${LevelZeroRuntime_LIBRARIES_PATH})
+
+    if(NOT TARGET LevelZeroRuntime::LevelZeroRuntime)
+        add_library(LevelZeroRuntime::LevelZeroRuntime INTERFACE IMPORTED)
+        set_target_properties(LevelZeroRuntime::LevelZeroRuntime
+            PROPERTIES INTERFACE_LINK_LIBRARIES "${LevelZeroRuntime_LIBRARIES}"
+        )
+        set_target_properties(LevelZeroRuntime::LevelZeroRuntime
+            PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LevelZeroRuntime_INCLUDE_DIRS}"
+        )
     endif()
 endif()
 
 # Check if a specific version of Level Zero is required
-if(LevelZero_FIND_VERSION)
+if(LevelZeroRuntime_FIND_VERSION)
     get_l0_loader_version()
     set(VERSION_GT_FIND_VERSION FALSE)
     compare_versions(
-        ${LevelZero_VERSION}
-        ${LevelZero_FIND_VERSION}
+        ${LevelZeroRuntime_VERSION}
+        ${LevelZeroRuntime_FIND_VERSION}
         VERSION_GT_FIND_VERSION
     )
+
     if(${VERSION_GT_FIND_VERSION})
-        set(LevelZero_FOUND TRUE)
+        set(LevelZeroRuntime_FOUND TRUE)
     else()
-        set(LevelZero_FOUND FALSE)
+        set(LevelZeroRuntime_FOUND FALSE)
     endif()
 else()
-    set(LevelZero_FOUND TRUE)
+    set(LevelZeroRuntime_FOUND TRUE)
 endif()
 
-find_package_handle_standard_args(LevelZero
+find_package_handle_standard_args(LevelZeroRuntime
     REQUIRED_VARS
-        LevelZero_FOUND
-        LevelZero_INCLUDE_DIRS
-        LevelZero_LIBRARY
-        LevelZero_LIBRARIES_DIR
+    LevelZeroRuntime_FOUND
+    LevelZeroRuntime_INCLUDE_DIRS
+    LevelZeroRuntime_LIBRARY
+    LevelZeroRuntime_LIBRARIES_DIR
     HANDLE_COMPONENTS
 )
-mark_as_advanced(LevelZero_LIBRARY LevelZero_INCLUDE_DIRS)
+mark_as_advanced(LevelZeroRuntime_LIBRARY LevelZeroRuntime_INCLUDE_DIRS)
 
-if(LevelZero_FOUND)
-    find_package_message(LevelZero "Found LevelZero: ${LevelZero_LIBRARY}"
-        "(found version ${LevelZero_VERSION})"
+if(LevelZeroRuntime_FOUND)
+    find_package_message(LevelZeroRuntime "Found LevelZero: ${LevelZeroRuntime_LIBRARY}"
+        "(found version ${LevelZeroRuntime_VERSION})"
     )
 else()
-    find_package_message(LevelZero "Could not find LevelZero" "")
+    find_package_message(LevelZeroRuntime "Could not find LevelZero" "")
 endif()
diff --git a/mlir/lib/ExecutionEngine/CMakeLists.txt b/mlir/lib/ExecutionEngine/CMakeLists.txt
index dd2ac75b88798..06c879f082926 100644
--- a/mlir/lib/ExecutionEngine/CMakeLists.txt
+++ b/mlir/lib/ExecutionEngine/CMakeLists.txt
@@ -14,12 +14,13 @@ set(LLVM_OPTIONAL_SOURCES
   RunnerUtils.cpp
   OptUtils.cpp
   JitRunner.cpp
+  LevelZeroRuntimeWrappers.cpp
   SpirvCpuRuntimeWrappers.cpp
   SyclRuntimeWrappers.cpp
   VulkanRuntimeWrappers.cpp
   VulkanRuntime.cpp
   VulkanRuntime.h
-  )
+)
 
 # Use a separate library for OptUtils, to avoid pulling in the entire JIT and
 # codegen infrastructure. Unlike MLIRExecutionEngine, this is part of
@@ -45,7 +46,7 @@ add_mlir_library(MLIRExecutionEngineUtils
   IPO
   Passes
   TargetParser
-  )
+)
 
 if(NOT MLIR_ENABLE_EXECUTION_ENGINE)
   return()
@@ -53,12 +54,12 @@ endif()
 
 if(LLVM_USE_INTEL_JITEVENTS)
   set(LLVM_JIT_LISTENER_LIB
-      IntelJITEvents)
+    IntelJITEvents)
 endif(LLVM_USE_INTEL_JITEVENTS)
 
 if(LLVM_USE_PERF)
   set(LLVM_JIT_LISTENER_LIB
-      PerfJITEvents)
+    PerfJITEvents)
 endif(LLVM_USE_PERF)
 
 add_mlir_library(MLIRExecutionEngine
@@ -91,7 +92,7 @@ add_mlir_library(MLIRExecutionEngine
   IPO
   Passes
   ${LLVM_JIT_LISTENER_LIB}
-  )
+)
 
 mlir_target_link_libraries(MLIRExecutionEngine PUBLIC
   MLIRBuiltinToLLVMIRTranslation
@@ -100,9 +101,9 @@ mlir_target_link_libraries(MLIRExecutionEngine PUBLIC
   MLIRLLVMToLLVMIRTranslation
   MLIROpenMPToLLVMIRTranslation
   MLIRTargetLLVMIRExport
-  )
+)
 
-if(LLVM_BUILD_LLVM_DYLIB AND NOT (WIN32 OR MINGW OR CYGWIN)) # Does not build on windows currently, see #106859
+if(LLVM_BUILD_LLVM_DYLIB AND NOT(WIN32 OR MINGW OR CYGWIN)) # Does not build on windows currently, see #106859
   # Build a shared library for the execution engine. Some downstream projects
   # use this library to build their own CPU runners while preserving dynamic
   # linkage.
@@ -122,7 +123,7 @@ if(LLVM_BUILD_LLVM_DYLIB AND NOT (WIN32 OR MINGW OR CYGWIN)) # Does not build on
     LINK_LIBS PUBLIC
     LLVM
     MLIR
-    )
+  )
 endif()
 
 get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
@@ -162,7 +163,7 @@ if(LLVM_ENABLE_PIC)
     Float16bits.cpp
 
     EXCLUDE_FROM_LIBMLIR
-    )
+  )
   set_property(TARGET mlir_float16_utils PROPERTY CXX_STANDARD 17)
   target_compile_definitions(mlir_float16_utils PRIVATE mlir_float16_utils_EXPORTS)
 
@@ -179,7 +180,7 @@ if(LLVM_ENABLE_PIC)
     mlir_float16_utils
     MLIRSparseTensorEnums
     MLIRSparseTensorRuntime
-    )
+  )
   set_property(TARGET mlir_c_runner_utils PROPERTY CXX_STANDARD 17)
   target_compile_definitions(mlir_c_runner_utils PRIVATE mlir_c_runner_utils_EXPORTS)
 
@@ -205,6 +206,7 @@ if(LLVM_ENABLE_PIC)
   )
   set_property(TARGET mlir_async_runtime PROPERTY CXX_VISIBILITY_PRESET hidden)
   target_compile_definitions(mlir_async_runtime PRIVATE mlir_async_runtime_EXPORTS)
+
   if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # Don't export symbols from link-time dependencies, these are internal
     # implementation details.
@@ -226,7 +228,8 @@ if(LLVM_ENABLE_PIC)
     # custom error message.
     include(CheckLanguage)
     check_language(CUDA)
-    if (CMAKE_CUDA_COMPILER)
+
+    if(CMAKE_CUDA_COMPILER)
       enable_language(CUDA)
     else()
       message(SEND_ERROR
@@ -290,13 +293,14 @@ if(LLVM_ENABLE_PIC)
 
   if(MLIR_ENABLE_ROCM_RUNNER)
     # Configure ROCm support.
-    if (NOT DEFINED ROCM_PATH)
-      if (NOT DEFINED ENV{ROCM_PATH})
+    if(NOT DEFINED ROCM_PATH)
+      if(NOT DEFINED ENV{ROCM_PATH})
         set(ROCM_PATH "/opt/rocm" CACHE PATH "Path to which ROCm has been installed")
       else()
         set(ROCM_PATH $ENV{ROCM_PATH} CACHE PATH "Path to which ROCm has been installed")
       endif()
     endif()
+
     # A lot of the ROCm CMake files expect to find their own dependencies in
     # CMAKE_PREFIX_PATH and don't respect PATHS or HINTS :( .
     # Therefore, temporarily add the ROCm path to CMAKE_PREFIX_PATH so we can
@@ -306,24 +310,28 @@ if(LLVM_ENABLE_PIC)
     find_package(hip REQUIRED)
     set(CMAKE_PREFIX_PATH "${REAL_CMAKE_PREFIX_PATH}")
 
-    if (NOT DEFINED ROCM_TEST_CHIPSET)
+    if(NOT DEFINED ROCM_TEST_CHIPSET)
       find_program(ROCM_AGENT_ENUMERATOR rocm_agent_enumerator "${ROCM_PATH}/bin" /usr/bin /usr/local/bin)
+
       if(ROCM_AGENT_ENUMERATOR)
-          execute_process(COMMAND "${ROCM_AGENT_ENUMERATOR}"
+        execute_process(COMMAND "${ROCM_AGENT_ENUMERATOR}"
           OUTPUT_VARIABLE AGENTS_STRING
           ERROR_VARIABLE AGENTS_STRING
           RESULT_VARIABLE AGENT_ENUMERATOR_RESULT)
       else()
         message(SEND_ERROR "Could not find rocm_agent_enumerator")
       endif()
-      if (NOT AGENT_ENUMERATOR_RESULT EQUAL 0)
+
+      if(NOT AGENT_ENUMERATOR_RESULT EQUAL 0)
         message(SEND_ERROR "Could not run rocm_agent_enumerator and ROCM_TEST_CHIPSET is not defined")
         set(AGENTS_STRING "")
       endif()
+
       string(STRIP AGENTS_STRING ${AGENTS_STRING})
       string(REPLACE "\n" ";" AGENTS_LIST ${AGENTS_STRING})
       list(FILTER AGENTS_LIST EXCLUDE REGEX "gfx000")
-      if (AGENTS_LIST STREQUAL "")
+
+      if(AGENTS_LIST STREQUAL "")
         message(SEND_ERROR "No non-CPU ROCm agents found on the system, and ROCM_TEST_CHIPSET is not defined")
       else()
         list(GET AGENTS_LIST 0 FIRST_AGENT)
@@ -342,27 +350,34 @@ if(LLVM_ENABLE_PIC)
     # Supress compiler warnings from HIP headers
     check_cxx_compiler_flag(-Wno-c++98-compat-extra-semi
       CXX_SUPPORTS_NO_CXX98_COMPAT_EXTRA_SEMI_FLAG)
-    if (CXX_SUPPORTS_CXX98_COMPAT_EXTRA_SEMI_FLAG)
+
+    if(CXX_SUPPORTS_CXX98_COMPAT_EXTRA_SEMI_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-c++98-compat-extra-semi")
     endif()
+
     check_cxx_compiler_flag(-Wno-return-type-c-linkage
-        CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
-    if (CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
+      CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
+
+    if(CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-return-type-c-linkage")
     endif()
+
     check_cxx_compiler_flag(-Wno-nested-anon-types
       CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
-    if (CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
+
+    if(CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-nested-anon-types")
     endif()
+
     check_cxx_compiler_flag(-Wno-gnu-anonymous-struct
       CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
-    if (CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
+
+    if(CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
-      "-Wno-gnu-anonymous-struct")
+        "-Wno-gnu-anonymous-struct")
     endif()
 
     set_property(TARGET mlir_rocm_runtime
@@ -381,9 +396,9 @@ if(LLVM_ENABLE_PIC)
       message(FATAL_ERROR "syclRuntime not found. Please set check oneapi installation and run setvars.sh.")
     endif()
 
-    find_package(LevelZero)
+    find_package(LevelZeroRuntime)
 
-    if(NOT LevelZero_FOUND)
+    if(NOT LevelZeroRuntime_FOUND)
       message(FATAL_ERROR "LevelZero not found. Please set LEVEL_ZERO_DIR.")
     endif()
 
@@ -395,18 +410,51 @@ if(LLVM_ENABLE_PIC)
     )
 
     check_cxx_compiler_flag("-frtti" CXX_HAS_FRTTI_FLAG)
+
     if(NOT CXX_HAS_FRTTI_FLAG)
       message(FATAL_ERROR "CXX compiler does not accept flag -frtti")
     endif()
-    target_compile_options (mlir_sycl_runtime PUBLIC -fexceptions -frtti)
+
+    target_compile_options(mlir_sycl_runtime PUBLIC -fexceptions -frtti)
 
     target_include_directories(mlir_sycl_runtime PRIVATE
       ${MLIR_INCLUDE_DIRS}
     )
 
-    target_link_libraries(mlir_sycl_runtime PRIVATE LevelZero::LevelZero SyclRuntime::SyclRuntime)
+    target_link_libraries(mlir_sycl_runtime PRIVATE LevelZeroRuntime::LevelZeroRuntime SyclRuntime::SyclRuntime)
+
+    set_property(TARGET mlir_sycl_runtime APPEND PROPERTY BUILD_RPATH "${LevelZeroRuntime_LIBRARIES_DIR}" "${SyclRuntime_LIBRARIES_DIR}")
+  endif()
+
+  if(MLIR_ENABLE_LEVEL_ZERO_RUNNER)
+    find_package(LevelZeroRuntime)
+
+    if(NOT LevelZeroRuntime_FOUND)
+      message(FATAL_ERROR "LevelZero not found. Please set LEVEL_ZERO_DIR.")
+    endif()
+
+    add_mlir_library(mlir_levelzero_runtime
+      SHARED
+      LevelZeroRuntimeWrappers.cpp
+
+      EXCLUDE_FROM_LIBMLIR
+    )
+
+    check_cxx_compiler_flag("-frtti" CXX_HAS_FRTTI_FLAG)
+
+    if(NOT CXX_HAS_FRTTI_FLAG)
+      message(FATAL_ERROR "CXX compiler does not accept flag -frtti")
+    endif()
+
+    target_compile_options(mlir_levelzero_runtime PUBLIC -fexceptions -frtti)
+
+    target_include_directories(mlir_levelzero_runtime PRIVATE
+      ${MLIR_INCLUDE_DIRS}
+    )
+
+    target_link_libraries(mlir_levelzero_runtime PRIVATE LevelZeroRuntime::LevelZeroRuntime)
 
-    set_property(TARGET mlir_sycl_runtime APPEND PROPERTY BUILD_RPATH "${LevelZero_LIBRARIES_DIR}" "${SyclRuntime_LIBRARIES_DIR}")
+    set_property(TARGET mlir_levelzero_runtime APPEND PROPERTY BUILD_RPATH "${LevelZeroRuntime_LIBRARIES_DIR}")
   endif()
 
   if(MLIR_ENABLE_SPIRV_CPU_RUNNER)
@@ -422,25 +470,26 @@ if(LLVM_ENABLE_PIC)
       mlir_spirv_cpu_runtime_EXPORTS)
   endif()
 
-  if (MLIR_ENABLE_VULKAN_RUNNER)
+  if(MLIR_ENABLE_VULKAN_RUNNER)
     find_package(Vulkan)
 
     # If Vulkan is not found try a path specified by VULKAN_SDK.
-    if (NOT Vulkan_FOUND)
-      if ("$ENV{VULKAN_SDK}" STREQUAL "")
+    if(NOT Vulkan_FOUND)
+      if("$ENV{VULKAN_SDK}" STREQUAL "")
         message(FATAL_ERROR "Vulkan not found through CMake; please provide "
-                            "VULKAN_SDK path as an environment variable")
+          "VULKAN_SDK path as an environment variable")
       endif()
 
       find_library(Vulkan_LIBRARY vulkan HINTS "$ENV{VULKAN_SDK}/lib" REQUIRED)
-      if (Vulkan_LIBRARY)
+
+      if(Vulkan_LIBRARY)
         set(Vulkan_FOUND ON)
         set(Vulkan_INCLUDE_DIR "$ENV{VULKAN_SDK}/include")
         message(STATUS "Found Vulkan: " ${Vulkan_LIBRARY})
       endif()
     endif()
 
-    if (NOT Vulkan_FOUND)
+    if(NOT Vulkan_FOUND)
       message(FATAL_ERROR "Cannot find Vulkan library")
     endif()
 
diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
new file mode 100644
index 0000000000000..70ac4761dc7fd
--- /dev/null
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -0,0 +1,491 @@
+//===- LevelZeroRuntimeWrappers.cpp - MLIR Level Zero (L0) wrapper library-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements wrappers around the Level Zero (L0) runtime library with C linkage
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Twine.h"
+
+#include <cassert>
+#include <deque>
+#include <exception>
+#include <functional>
+#include <iostream>
+#include <level_zero/ze_api.h>
+#include <limits>
+#include <unordered_set>
+#include <vector>
+
+namespace {
+
+template <typename F>
+auto catchAll(F &&func) {
+  try {
+    return func();
+  } catch (const std::exception &e) {
+    std::cerr << "An exception was thrown: " << e.what() << std::endl;
+    std::abort();
+  } catch (...) {
+    std::cerr << "An unknown exception was thrown." << std::endl;
+    std::abort();
+  }
+}
+
+#define L0_SAFE_CALL(call)                                                     \
+  {                                                                            \
+    ze_result_t status = (call);                                               \
+    if (status != ZE_RESULT_SUCCESS) {                                         \
+      std::cerr << "L0 error " << status << std::endl;                         \
+      std::abort();                                                            \
+    }                                                                          \
+  }
+
+} // namespace
+
+//===-------...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-mlir-execution-engine

Author: Md Abdullah Shahneous Bari (mshahneo)

Changes

Adds LevelZeroRuntime wrapper and tests.


Patch is 53.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151038.diff

8 Files Affected:

  • (renamed) mlir/cmake/modules/FindLevelZeroRuntime.cmake (+60-53)
  • (modified) mlir/lib/ExecutionEngine/CMakeLists.txt (+83-34)
  • (added) mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp (+491)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-addf32-to-spirv.mlir (+56)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-addi64-to-spirv.mlir (+54)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-memcpy-addf32-to-spirv.mlir (+53)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-reluf32-to-spirv.mlir (+79)
  • (added) mlir/test/Integration/GPU/LEVELZERO/lit.local.cfg (+2)
diff --git a/mlir/cmake/modules/FindLevelZero.cmake b/mlir/cmake/modules/FindLevelZeroRuntime.cmake
similarity index 66%
rename from mlir/cmake/modules/FindLevelZero.cmake
rename to mlir/cmake/modules/FindLevelZeroRuntime.cmake
index 012187f0afc0b..b1e8e5b6387f2 100644
--- a/mlir/cmake/modules/FindLevelZero.cmake
+++ b/mlir/cmake/modules/FindLevelZeroRuntime.cmake
@@ -20,7 +20,6 @@ include(FindPackageHandleStandardArgs)
 # Search path priority
 # 1. CMake Variable LEVEL_ZERO_DIR
 # 2. Environment Variable LEVEL_ZERO_DIR
-
 if(NOT LEVEL_ZERO_DIR)
     if(DEFINED ENV{LEVEL_ZERO_DIR})
         set(LEVEL_ZERO_DIR "$ENV{LEVEL_ZERO_DIR}")
@@ -28,32 +27,32 @@ if(NOT LEVEL_ZERO_DIR)
 endif()
 
 if(LEVEL_ZERO_DIR)
-    find_path(LevelZero_INCLUDE_DIR
+    find_path(LevelZeroRuntime_INCLUDE_DIR
         NAMES level_zero/ze_api.h
         PATHS ${LEVEL_ZERO_DIR}/include
         NO_DEFAULT_PATH
     )
 
     if(LINUX)
-        find_library(LevelZero_LIBRARY
+        find_library(LevelZeroRuntime_LIBRARY
             NAMES ze_loader
             PATHS ${LEVEL_ZERO_DIR}/lib
-                  ${LEVEL_ZERO_DIR}/lib/x86_64-linux-gnu
+            ${LEVEL_ZERO_DIR}/lib/x86_64-linux-gnu
             NO_DEFAULT_PATH
         )
     else()
-        find_library(LevelZero_LIBRARY
+        find_library(LevelZeroRuntime_LIBRARY
             NAMES ze_loader
             PATHS ${LEVEL_ZERO_DIR}/lib
             NO_DEFAULT_PATH
         )
     endif()
 else()
-    find_path(LevelZero_INCLUDE_DIR
+    find_path(LevelZeroRuntime_INCLUDE_DIR
         NAMES level_zero/ze_api.h
     )
 
-    find_library(LevelZero_LIBRARY
+    find_library(LevelZeroRuntime_LIBRARY
         NAMES ze_loader
     )
 endif()
@@ -64,26 +63,33 @@ endif()
 # lists of equal lengths, with the shorter string getting zero-padded.
 function(compare_versions VERSION_STR1 VERSION_STR2 OUTPUT)
     # Convert the strings to list
-    string(REPLACE  "." ";" VL1 ${VERSION_STR1})
-    string(REPLACE  "." ";" VL2 ${VERSION_STR2})
+    string(REPLACE "." ";" VL1 ${VERSION_STR1})
+    string(REPLACE "." ";" VL2 ${VERSION_STR2})
+
     # get lengths of both lists
     list(LENGTH VL1 VL1_LEN)
     list(LENGTH VL2 VL2_LEN)
     set(LEN ${VL1_LEN})
+
     # If they differ in size pad the shorter list with 0s
     if(VL1_LEN GREATER VL2_LEN)
         math(EXPR DIFF "${VL1_LEN} - ${VL2_LEN}" OUTPUT_FORMAT DECIMAL)
+
         foreach(IDX RANGE 1 ${DIFF} 1)
             list(APPEND VL2 "0")
         endforeach()
     elseif(VL2_LEN GREATER VL2_LEN)
         math(EXPR DIFF "${VL1_LEN} - ${VL2_LEN}" OUTPUT_FORMAT DECIMAL)
+
         foreach(IDX RANGE 1 ${DIFF} 1)
             list(APPEND VL2 "0")
         endforeach()
+
         set(LEN ${VL2_LEN})
     endif()
+
     math(EXPR LEN_SUB_ONE "${LEN}-1")
+
     foreach(IDX RANGE 0 ${LEN_SUB_ONE} 1)
         list(GET VL1 ${IDX} VAL1)
         list(GET VL2 ${IDX} VAL2)
@@ -98,12 +104,10 @@ function(compare_versions VERSION_STR1 VERSION_STR2 OUTPUT)
             set(${OUTPUT} TRUE PARENT_SCOPE)
         endif()
     endforeach()
-
-    endfunction(compare_versions)
+endfunction(compare_versions)
 
 # Creates a small function to run and extract the LevelZero loader version.
 function(get_l0_loader_version)
-
     set(L0_VERSIONEER_SRC
         [====[
         #include <iostream>
@@ -142,19 +146,20 @@ function(get_l0_loader_version)
 
     # We need both the directories in the include path as ze_loader.h
     # includes "ze_api.h" and not "level_zero/ze_api.h".
-    list(APPEND INCLUDE_DIRS ${LevelZero_INCLUDE_DIR})
-    list(APPEND INCLUDE_DIRS ${LevelZero_INCLUDE_DIR}/level_zero)
+    list(APPEND INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR})
+    list(APPEND INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR}/level_zero)
     list(JOIN INCLUDE_DIRS ";" INCLUDE_DIRS_STR)
     try_run(L0_VERSIONEER_RUN L0_VERSIONEER_COMPILE
-            "${CMAKE_BINARY_DIR}"
-            "${L0_VERSIONEER_FILE}"
-            LINK_LIBRARIES ${LevelZero_LIBRARY}
-            CMAKE_FLAGS
-                "-DINCLUDE_DIRECTORIES=${INCLUDE_DIRS_STR}"
-            RUN_OUTPUT_VARIABLE L0_VERSION
+        "${CMAKE_BINARY_DIR}"
+        "${L0_VERSIONEER_FILE}"
+        LINK_LIBRARIES ${LevelZeroRuntime_LIBRARY}
+        CMAKE_FLAGS
+        "-DINCLUDE_DIRECTORIES=${INCLUDE_DIRS_STR}"
+        RUN_OUTPUT_VARIABLE L0_VERSION
     )
-    if(${L0_VERSIONEER_COMPILE} AND (DEFINED L0_VERSIONEER_RUN))
-        set(LevelZero_VERSION ${L0_VERSION} PARENT_SCOPE)
+
+    if(${L0_VERSIONEER_COMPILE} AND(DEFINED L0_VERSIONEER_RUN))
+        set(LevelZeroRuntime_VERSION ${L0_VERSION} PARENT_SCOPE)
         message(STATUS "Found Level Zero of version: ${L0_VERSION}")
     else()
         message(FATAL_ERROR
@@ -163,59 +168,61 @@ function(get_l0_loader_version)
     endif()
 endfunction(get_l0_loader_version)
 
-if(LevelZero_INCLUDE_DIR AND LevelZero_LIBRARY)
-    list(APPEND LevelZero_LIBRARIES "${LevelZero_LIBRARY}")
-    list(APPEND LevelZero_INCLUDE_DIRS ${LevelZero_INCLUDE_DIR})
+if(LevelZeroRuntime_INCLUDE_DIR AND LevelZeroRuntime_LIBRARY)
+    list(APPEND LevelZeroRuntime_LIBRARIES "${LevelZeroRuntime_LIBRARY}")
+    list(APPEND LevelZeroRuntime_INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR})
+
     if(OpenCL_FOUND)
-      list(APPEND LevelZero_INCLUDE_DIRS ${OpenCL_INCLUDE_DIRS})
+        list(APPEND LevelZeroRuntime_INCLUDE_DIRS ${OpenCL_INCLUDE_DIRS})
     endif()
 
-    cmake_path(GET LevelZero_LIBRARY PARENT_PATH LevelZero_LIBRARIES_PATH)
-    set(LevelZero_LIBRARIES_DIR ${LevelZero_LIBRARIES_PATH})
-
-    if(NOT TARGET LevelZero::LevelZero)
-      add_library(LevelZero::LevelZero INTERFACE IMPORTED)
-      set_target_properties(LevelZero::LevelZero
-        PROPERTIES INTERFACE_LINK_LIBRARIES "${LevelZero_LIBRARIES}"
-      )
-      set_target_properties(LevelZero::LevelZero
-        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LevelZero_INCLUDE_DIRS}"
-      )
+    cmake_path(GET LevelZeroRuntime_LIBRARY PARENT_PATH LevelZeroRuntime_LIBRARIES_PATH)
+    set(LevelZeroRuntime_LIBRARIES_DIR ${LevelZeroRuntime_LIBRARIES_PATH})
+
+    if(NOT TARGET LevelZeroRuntime::LevelZeroRuntime)
+        add_library(LevelZeroRuntime::LevelZeroRuntime INTERFACE IMPORTED)
+        set_target_properties(LevelZeroRuntime::LevelZeroRuntime
+            PROPERTIES INTERFACE_LINK_LIBRARIES "${LevelZeroRuntime_LIBRARIES}"
+        )
+        set_target_properties(LevelZeroRuntime::LevelZeroRuntime
+            PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LevelZeroRuntime_INCLUDE_DIRS}"
+        )
     endif()
 endif()
 
 # Check if a specific version of Level Zero is required
-if(LevelZero_FIND_VERSION)
+if(LevelZeroRuntime_FIND_VERSION)
     get_l0_loader_version()
     set(VERSION_GT_FIND_VERSION FALSE)
     compare_versions(
-        ${LevelZero_VERSION}
-        ${LevelZero_FIND_VERSION}
+        ${LevelZeroRuntime_VERSION}
+        ${LevelZeroRuntime_FIND_VERSION}
         VERSION_GT_FIND_VERSION
     )
+
     if(${VERSION_GT_FIND_VERSION})
-        set(LevelZero_FOUND TRUE)
+        set(LevelZeroRuntime_FOUND TRUE)
     else()
-        set(LevelZero_FOUND FALSE)
+        set(LevelZeroRuntime_FOUND FALSE)
     endif()
 else()
-    set(LevelZero_FOUND TRUE)
+    set(LevelZeroRuntime_FOUND TRUE)
 endif()
 
-find_package_handle_standard_args(LevelZero
+find_package_handle_standard_args(LevelZeroRuntime
     REQUIRED_VARS
-        LevelZero_FOUND
-        LevelZero_INCLUDE_DIRS
-        LevelZero_LIBRARY
-        LevelZero_LIBRARIES_DIR
+    LevelZeroRuntime_FOUND
+    LevelZeroRuntime_INCLUDE_DIRS
+    LevelZeroRuntime_LIBRARY
+    LevelZeroRuntime_LIBRARIES_DIR
     HANDLE_COMPONENTS
 )
-mark_as_advanced(LevelZero_LIBRARY LevelZero_INCLUDE_DIRS)
+mark_as_advanced(LevelZeroRuntime_LIBRARY LevelZeroRuntime_INCLUDE_DIRS)
 
-if(LevelZero_FOUND)
-    find_package_message(LevelZero "Found LevelZero: ${LevelZero_LIBRARY}"
-        "(found version ${LevelZero_VERSION})"
+if(LevelZeroRuntime_FOUND)
+    find_package_message(LevelZeroRuntime "Found LevelZero: ${LevelZeroRuntime_LIBRARY}"
+        "(found version ${LevelZeroRuntime_VERSION})"
     )
 else()
-    find_package_message(LevelZero "Could not find LevelZero" "")
+    find_package_message(LevelZeroRuntime "Could not find LevelZero" "")
 endif()
diff --git a/mlir/lib/ExecutionEngine/CMakeLists.txt b/mlir/lib/ExecutionEngine/CMakeLists.txt
index dd2ac75b88798..06c879f082926 100644
--- a/mlir/lib/ExecutionEngine/CMakeLists.txt
+++ b/mlir/lib/ExecutionEngine/CMakeLists.txt
@@ -14,12 +14,13 @@ set(LLVM_OPTIONAL_SOURCES
   RunnerUtils.cpp
   OptUtils.cpp
   JitRunner.cpp
+  LevelZeroRuntimeWrappers.cpp
   SpirvCpuRuntimeWrappers.cpp
   SyclRuntimeWrappers.cpp
   VulkanRuntimeWrappers.cpp
   VulkanRuntime.cpp
   VulkanRuntime.h
-  )
+)
 
 # Use a separate library for OptUtils, to avoid pulling in the entire JIT and
 # codegen infrastructure. Unlike MLIRExecutionEngine, this is part of
@@ -45,7 +46,7 @@ add_mlir_library(MLIRExecutionEngineUtils
   IPO
   Passes
   TargetParser
-  )
+)
 
 if(NOT MLIR_ENABLE_EXECUTION_ENGINE)
   return()
@@ -53,12 +54,12 @@ endif()
 
 if(LLVM_USE_INTEL_JITEVENTS)
   set(LLVM_JIT_LISTENER_LIB
-      IntelJITEvents)
+    IntelJITEvents)
 endif(LLVM_USE_INTEL_JITEVENTS)
 
 if(LLVM_USE_PERF)
   set(LLVM_JIT_LISTENER_LIB
-      PerfJITEvents)
+    PerfJITEvents)
 endif(LLVM_USE_PERF)
 
 add_mlir_library(MLIRExecutionEngine
@@ -91,7 +92,7 @@ add_mlir_library(MLIRExecutionEngine
   IPO
   Passes
   ${LLVM_JIT_LISTENER_LIB}
-  )
+)
 
 mlir_target_link_libraries(MLIRExecutionEngine PUBLIC
   MLIRBuiltinToLLVMIRTranslation
@@ -100,9 +101,9 @@ mlir_target_link_libraries(MLIRExecutionEngine PUBLIC
   MLIRLLVMToLLVMIRTranslation
   MLIROpenMPToLLVMIRTranslation
   MLIRTargetLLVMIRExport
-  )
+)
 
-if(LLVM_BUILD_LLVM_DYLIB AND NOT (WIN32 OR MINGW OR CYGWIN)) # Does not build on windows currently, see #106859
+if(LLVM_BUILD_LLVM_DYLIB AND NOT(WIN32 OR MINGW OR CYGWIN)) # Does not build on windows currently, see #106859
   # Build a shared library for the execution engine. Some downstream projects
   # use this library to build their own CPU runners while preserving dynamic
   # linkage.
@@ -122,7 +123,7 @@ if(LLVM_BUILD_LLVM_DYLIB AND NOT (WIN32 OR MINGW OR CYGWIN)) # Does not build on
     LINK_LIBS PUBLIC
     LLVM
     MLIR
-    )
+  )
 endif()
 
 get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
@@ -162,7 +163,7 @@ if(LLVM_ENABLE_PIC)
     Float16bits.cpp
 
     EXCLUDE_FROM_LIBMLIR
-    )
+  )
   set_property(TARGET mlir_float16_utils PROPERTY CXX_STANDARD 17)
   target_compile_definitions(mlir_float16_utils PRIVATE mlir_float16_utils_EXPORTS)
 
@@ -179,7 +180,7 @@ if(LLVM_ENABLE_PIC)
     mlir_float16_utils
     MLIRSparseTensorEnums
     MLIRSparseTensorRuntime
-    )
+  )
   set_property(TARGET mlir_c_runner_utils PROPERTY CXX_STANDARD 17)
   target_compile_definitions(mlir_c_runner_utils PRIVATE mlir_c_runner_utils_EXPORTS)
 
@@ -205,6 +206,7 @@ if(LLVM_ENABLE_PIC)
   )
   set_property(TARGET mlir_async_runtime PROPERTY CXX_VISIBILITY_PRESET hidden)
   target_compile_definitions(mlir_async_runtime PRIVATE mlir_async_runtime_EXPORTS)
+
   if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # Don't export symbols from link-time dependencies, these are internal
     # implementation details.
@@ -226,7 +228,8 @@ if(LLVM_ENABLE_PIC)
     # custom error message.
     include(CheckLanguage)
     check_language(CUDA)
-    if (CMAKE_CUDA_COMPILER)
+
+    if(CMAKE_CUDA_COMPILER)
       enable_language(CUDA)
     else()
       message(SEND_ERROR
@@ -290,13 +293,14 @@ if(LLVM_ENABLE_PIC)
 
   if(MLIR_ENABLE_ROCM_RUNNER)
     # Configure ROCm support.
-    if (NOT DEFINED ROCM_PATH)
-      if (NOT DEFINED ENV{ROCM_PATH})
+    if(NOT DEFINED ROCM_PATH)
+      if(NOT DEFINED ENV{ROCM_PATH})
         set(ROCM_PATH "/opt/rocm" CACHE PATH "Path to which ROCm has been installed")
       else()
         set(ROCM_PATH $ENV{ROCM_PATH} CACHE PATH "Path to which ROCm has been installed")
       endif()
     endif()
+
     # A lot of the ROCm CMake files expect to find their own dependencies in
     # CMAKE_PREFIX_PATH and don't respect PATHS or HINTS :( .
     # Therefore, temporarily add the ROCm path to CMAKE_PREFIX_PATH so we can
@@ -306,24 +310,28 @@ if(LLVM_ENABLE_PIC)
     find_package(hip REQUIRED)
     set(CMAKE_PREFIX_PATH "${REAL_CMAKE_PREFIX_PATH}")
 
-    if (NOT DEFINED ROCM_TEST_CHIPSET)
+    if(NOT DEFINED ROCM_TEST_CHIPSET)
       find_program(ROCM_AGENT_ENUMERATOR rocm_agent_enumerator "${ROCM_PATH}/bin" /usr/bin /usr/local/bin)
+
       if(ROCM_AGENT_ENUMERATOR)
-          execute_process(COMMAND "${ROCM_AGENT_ENUMERATOR}"
+        execute_process(COMMAND "${ROCM_AGENT_ENUMERATOR}"
           OUTPUT_VARIABLE AGENTS_STRING
           ERROR_VARIABLE AGENTS_STRING
           RESULT_VARIABLE AGENT_ENUMERATOR_RESULT)
       else()
         message(SEND_ERROR "Could not find rocm_agent_enumerator")
       endif()
-      if (NOT AGENT_ENUMERATOR_RESULT EQUAL 0)
+
+      if(NOT AGENT_ENUMERATOR_RESULT EQUAL 0)
         message(SEND_ERROR "Could not run rocm_agent_enumerator and ROCM_TEST_CHIPSET is not defined")
         set(AGENTS_STRING "")
       endif()
+
       string(STRIP AGENTS_STRING ${AGENTS_STRING})
       string(REPLACE "\n" ";" AGENTS_LIST ${AGENTS_STRING})
       list(FILTER AGENTS_LIST EXCLUDE REGEX "gfx000")
-      if (AGENTS_LIST STREQUAL "")
+
+      if(AGENTS_LIST STREQUAL "")
         message(SEND_ERROR "No non-CPU ROCm agents found on the system, and ROCM_TEST_CHIPSET is not defined")
       else()
         list(GET AGENTS_LIST 0 FIRST_AGENT)
@@ -342,27 +350,34 @@ if(LLVM_ENABLE_PIC)
     # Supress compiler warnings from HIP headers
     check_cxx_compiler_flag(-Wno-c++98-compat-extra-semi
       CXX_SUPPORTS_NO_CXX98_COMPAT_EXTRA_SEMI_FLAG)
-    if (CXX_SUPPORTS_CXX98_COMPAT_EXTRA_SEMI_FLAG)
+
+    if(CXX_SUPPORTS_CXX98_COMPAT_EXTRA_SEMI_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-c++98-compat-extra-semi")
     endif()
+
     check_cxx_compiler_flag(-Wno-return-type-c-linkage
-        CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
-    if (CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
+      CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
+
+    if(CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-return-type-c-linkage")
     endif()
+
     check_cxx_compiler_flag(-Wno-nested-anon-types
       CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
-    if (CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
+
+    if(CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-nested-anon-types")
     endif()
+
     check_cxx_compiler_flag(-Wno-gnu-anonymous-struct
       CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
-    if (CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
+
+    if(CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
-      "-Wno-gnu-anonymous-struct")
+        "-Wno-gnu-anonymous-struct")
     endif()
 
     set_property(TARGET mlir_rocm_runtime
@@ -381,9 +396,9 @@ if(LLVM_ENABLE_PIC)
       message(FATAL_ERROR "syclRuntime not found. Please set check oneapi installation and run setvars.sh.")
     endif()
 
-    find_package(LevelZero)
+    find_package(LevelZeroRuntime)
 
-    if(NOT LevelZero_FOUND)
+    if(NOT LevelZeroRuntime_FOUND)
       message(FATAL_ERROR "LevelZero not found. Please set LEVEL_ZERO_DIR.")
     endif()
 
@@ -395,18 +410,51 @@ if(LLVM_ENABLE_PIC)
     )
 
     check_cxx_compiler_flag("-frtti" CXX_HAS_FRTTI_FLAG)
+
     if(NOT CXX_HAS_FRTTI_FLAG)
       message(FATAL_ERROR "CXX compiler does not accept flag -frtti")
     endif()
-    target_compile_options (mlir_sycl_runtime PUBLIC -fexceptions -frtti)
+
+    target_compile_options(mlir_sycl_runtime PUBLIC -fexceptions -frtti)
 
     target_include_directories(mlir_sycl_runtime PRIVATE
       ${MLIR_INCLUDE_DIRS}
     )
 
-    target_link_libraries(mlir_sycl_runtime PRIVATE LevelZero::LevelZero SyclRuntime::SyclRuntime)
+    target_link_libraries(mlir_sycl_runtime PRIVATE LevelZeroRuntime::LevelZeroRuntime SyclRuntime::SyclRuntime)
+
+    set_property(TARGET mlir_sycl_runtime APPEND PROPERTY BUILD_RPATH "${LevelZeroRuntime_LIBRARIES_DIR}" "${SyclRuntime_LIBRARIES_DIR}")
+  endif()
+
+  if(MLIR_ENABLE_LEVEL_ZERO_RUNNER)
+    find_package(LevelZeroRuntime)
+
+    if(NOT LevelZeroRuntime_FOUND)
+      message(FATAL_ERROR "LevelZero not found. Please set LEVEL_ZERO_DIR.")
+    endif()
+
+    add_mlir_library(mlir_levelzero_runtime
+      SHARED
+      LevelZeroRuntimeWrappers.cpp
+
+      EXCLUDE_FROM_LIBMLIR
+    )
+
+    check_cxx_compiler_flag("-frtti" CXX_HAS_FRTTI_FLAG)
+
+    if(NOT CXX_HAS_FRTTI_FLAG)
+      message(FATAL_ERROR "CXX compiler does not accept flag -frtti")
+    endif()
+
+    target_compile_options(mlir_levelzero_runtime PUBLIC -fexceptions -frtti)
+
+    target_include_directories(mlir_levelzero_runtime PRIVATE
+      ${MLIR_INCLUDE_DIRS}
+    )
+
+    target_link_libraries(mlir_levelzero_runtime PRIVATE LevelZeroRuntime::LevelZeroRuntime)
 
-    set_property(TARGET mlir_sycl_runtime APPEND PROPERTY BUILD_RPATH "${LevelZero_LIBRARIES_DIR}" "${SyclRuntime_LIBRARIES_DIR}")
+    set_property(TARGET mlir_levelzero_runtime APPEND PROPERTY BUILD_RPATH "${LevelZeroRuntime_LIBRARIES_DIR}")
   endif()
 
   if(MLIR_ENABLE_SPIRV_CPU_RUNNER)
@@ -422,25 +470,26 @@ if(LLVM_ENABLE_PIC)
       mlir_spirv_cpu_runtime_EXPORTS)
   endif()
 
-  if (MLIR_ENABLE_VULKAN_RUNNER)
+  if(MLIR_ENABLE_VULKAN_RUNNER)
     find_package(Vulkan)
 
     # If Vulkan is not found try a path specified by VULKAN_SDK.
-    if (NOT Vulkan_FOUND)
-      if ("$ENV{VULKAN_SDK}" STREQUAL "")
+    if(NOT Vulkan_FOUND)
+      if("$ENV{VULKAN_SDK}" STREQUAL "")
         message(FATAL_ERROR "Vulkan not found through CMake; please provide "
-                            "VULKAN_SDK path as an environment variable")
+          "VULKAN_SDK path as an environment variable")
       endif()
 
       find_library(Vulkan_LIBRARY vulkan HINTS "$ENV{VULKAN_SDK}/lib" REQUIRED)
-      if (Vulkan_LIBRARY)
+
+      if(Vulkan_LIBRARY)
         set(Vulkan_FOUND ON)
         set(Vulkan_INCLUDE_DIR "$ENV{VULKAN_SDK}/include")
         message(STATUS "Found Vulkan: " ${Vulkan_LIBRARY})
       endif()
     endif()
 
-    if (NOT Vulkan_FOUND)
+    if(NOT Vulkan_FOUND)
       message(FATAL_ERROR "Cannot find Vulkan library")
     endif()
 
diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
new file mode 100644
index 0000000000000..70ac4761dc7fd
--- /dev/null
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -0,0 +1,491 @@
+//===- LevelZeroRuntimeWrappers.cpp - MLIR Level Zero (L0) wrapper library-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements wrappers around the Level Zero (L0) runtime library with C linkage
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Twine.h"
+
+#include <cassert>
+#include <deque>
+#include <exception>
+#include <functional>
+#include <iostream>
+#include <level_zero/ze_api.h>
+#include <limits>
+#include <unordered_set>
+#include <vector>
+
+namespace {
+
+template <typename F>
+auto catchAll(F &&func) {
+  try {
+    return func();
+  } catch (const std::exception &e) {
+    std::cerr << "An exception was thrown: " << e.what() << std::endl;
+    std::abort();
+  } catch (...) {
+    std::cerr << "An unknown exception was thrown." << std::endl;
+    std::abort();
+  }
+}
+
+#define L0_SAFE_CALL(call)                                                     \
+  {                                                                            \
+    ze_result_t status = (call);                                               \
+    if (status != ZE_RESULT_SUCCESS) {                                         \
+      std::cerr << "L0 error " << status << std::endl;                         \
+      std::abort();                                                            \
+    }                                                                          \
+  }
+
+} // namespace
+
+//===-------...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-mlir

Author: Md Abdullah Shahneous Bari (mshahneo)

Changes

Adds LevelZeroRuntime wrapper and tests.


Patch is 53.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151038.diff

8 Files Affected:

  • (renamed) mlir/cmake/modules/FindLevelZeroRuntime.cmake (+60-53)
  • (modified) mlir/lib/ExecutionEngine/CMakeLists.txt (+83-34)
  • (added) mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp (+491)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-addf32-to-spirv.mlir (+56)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-addi64-to-spirv.mlir (+54)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-memcpy-addf32-to-spirv.mlir (+53)
  • (added) mlir/test/Integration/GPU/LEVELZERO/gpu-reluf32-to-spirv.mlir (+79)
  • (added) mlir/test/Integration/GPU/LEVELZERO/lit.local.cfg (+2)
diff --git a/mlir/cmake/modules/FindLevelZero.cmake b/mlir/cmake/modules/FindLevelZeroRuntime.cmake
similarity index 66%
rename from mlir/cmake/modules/FindLevelZero.cmake
rename to mlir/cmake/modules/FindLevelZeroRuntime.cmake
index 012187f0afc0b..b1e8e5b6387f2 100644
--- a/mlir/cmake/modules/FindLevelZero.cmake
+++ b/mlir/cmake/modules/FindLevelZeroRuntime.cmake
@@ -20,7 +20,6 @@ include(FindPackageHandleStandardArgs)
 # Search path priority
 # 1. CMake Variable LEVEL_ZERO_DIR
 # 2. Environment Variable LEVEL_ZERO_DIR
-
 if(NOT LEVEL_ZERO_DIR)
     if(DEFINED ENV{LEVEL_ZERO_DIR})
         set(LEVEL_ZERO_DIR "$ENV{LEVEL_ZERO_DIR}")
@@ -28,32 +27,32 @@ if(NOT LEVEL_ZERO_DIR)
 endif()
 
 if(LEVEL_ZERO_DIR)
-    find_path(LevelZero_INCLUDE_DIR
+    find_path(LevelZeroRuntime_INCLUDE_DIR
         NAMES level_zero/ze_api.h
         PATHS ${LEVEL_ZERO_DIR}/include
         NO_DEFAULT_PATH
     )
 
     if(LINUX)
-        find_library(LevelZero_LIBRARY
+        find_library(LevelZeroRuntime_LIBRARY
             NAMES ze_loader
             PATHS ${LEVEL_ZERO_DIR}/lib
-                  ${LEVEL_ZERO_DIR}/lib/x86_64-linux-gnu
+            ${LEVEL_ZERO_DIR}/lib/x86_64-linux-gnu
             NO_DEFAULT_PATH
         )
     else()
-        find_library(LevelZero_LIBRARY
+        find_library(LevelZeroRuntime_LIBRARY
             NAMES ze_loader
             PATHS ${LEVEL_ZERO_DIR}/lib
             NO_DEFAULT_PATH
         )
     endif()
 else()
-    find_path(LevelZero_INCLUDE_DIR
+    find_path(LevelZeroRuntime_INCLUDE_DIR
         NAMES level_zero/ze_api.h
     )
 
-    find_library(LevelZero_LIBRARY
+    find_library(LevelZeroRuntime_LIBRARY
         NAMES ze_loader
     )
 endif()
@@ -64,26 +63,33 @@ endif()
 # lists of equal lengths, with the shorter string getting zero-padded.
 function(compare_versions VERSION_STR1 VERSION_STR2 OUTPUT)
     # Convert the strings to list
-    string(REPLACE  "." ";" VL1 ${VERSION_STR1})
-    string(REPLACE  "." ";" VL2 ${VERSION_STR2})
+    string(REPLACE "." ";" VL1 ${VERSION_STR1})
+    string(REPLACE "." ";" VL2 ${VERSION_STR2})
+
     # get lengths of both lists
     list(LENGTH VL1 VL1_LEN)
     list(LENGTH VL2 VL2_LEN)
     set(LEN ${VL1_LEN})
+
     # If they differ in size pad the shorter list with 0s
     if(VL1_LEN GREATER VL2_LEN)
         math(EXPR DIFF "${VL1_LEN} - ${VL2_LEN}" OUTPUT_FORMAT DECIMAL)
+
         foreach(IDX RANGE 1 ${DIFF} 1)
             list(APPEND VL2 "0")
         endforeach()
     elseif(VL2_LEN GREATER VL2_LEN)
         math(EXPR DIFF "${VL1_LEN} - ${VL2_LEN}" OUTPUT_FORMAT DECIMAL)
+
         foreach(IDX RANGE 1 ${DIFF} 1)
             list(APPEND VL2 "0")
         endforeach()
+
         set(LEN ${VL2_LEN})
     endif()
+
     math(EXPR LEN_SUB_ONE "${LEN}-1")
+
     foreach(IDX RANGE 0 ${LEN_SUB_ONE} 1)
         list(GET VL1 ${IDX} VAL1)
         list(GET VL2 ${IDX} VAL2)
@@ -98,12 +104,10 @@ function(compare_versions VERSION_STR1 VERSION_STR2 OUTPUT)
             set(${OUTPUT} TRUE PARENT_SCOPE)
         endif()
     endforeach()
-
-    endfunction(compare_versions)
+endfunction(compare_versions)
 
 # Creates a small function to run and extract the LevelZero loader version.
 function(get_l0_loader_version)
-
     set(L0_VERSIONEER_SRC
         [====[
         #include <iostream>
@@ -142,19 +146,20 @@ function(get_l0_loader_version)
 
     # We need both the directories in the include path as ze_loader.h
     # includes "ze_api.h" and not "level_zero/ze_api.h".
-    list(APPEND INCLUDE_DIRS ${LevelZero_INCLUDE_DIR})
-    list(APPEND INCLUDE_DIRS ${LevelZero_INCLUDE_DIR}/level_zero)
+    list(APPEND INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR})
+    list(APPEND INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR}/level_zero)
     list(JOIN INCLUDE_DIRS ";" INCLUDE_DIRS_STR)
     try_run(L0_VERSIONEER_RUN L0_VERSIONEER_COMPILE
-            "${CMAKE_BINARY_DIR}"
-            "${L0_VERSIONEER_FILE}"
-            LINK_LIBRARIES ${LevelZero_LIBRARY}
-            CMAKE_FLAGS
-                "-DINCLUDE_DIRECTORIES=${INCLUDE_DIRS_STR}"
-            RUN_OUTPUT_VARIABLE L0_VERSION
+        "${CMAKE_BINARY_DIR}"
+        "${L0_VERSIONEER_FILE}"
+        LINK_LIBRARIES ${LevelZeroRuntime_LIBRARY}
+        CMAKE_FLAGS
+        "-DINCLUDE_DIRECTORIES=${INCLUDE_DIRS_STR}"
+        RUN_OUTPUT_VARIABLE L0_VERSION
     )
-    if(${L0_VERSIONEER_COMPILE} AND (DEFINED L0_VERSIONEER_RUN))
-        set(LevelZero_VERSION ${L0_VERSION} PARENT_SCOPE)
+
+    if(${L0_VERSIONEER_COMPILE} AND(DEFINED L0_VERSIONEER_RUN))
+        set(LevelZeroRuntime_VERSION ${L0_VERSION} PARENT_SCOPE)
         message(STATUS "Found Level Zero of version: ${L0_VERSION}")
     else()
         message(FATAL_ERROR
@@ -163,59 +168,61 @@ function(get_l0_loader_version)
     endif()
 endfunction(get_l0_loader_version)
 
-if(LevelZero_INCLUDE_DIR AND LevelZero_LIBRARY)
-    list(APPEND LevelZero_LIBRARIES "${LevelZero_LIBRARY}")
-    list(APPEND LevelZero_INCLUDE_DIRS ${LevelZero_INCLUDE_DIR})
+if(LevelZeroRuntime_INCLUDE_DIR AND LevelZeroRuntime_LIBRARY)
+    list(APPEND LevelZeroRuntime_LIBRARIES "${LevelZeroRuntime_LIBRARY}")
+    list(APPEND LevelZeroRuntime_INCLUDE_DIRS ${LevelZeroRuntime_INCLUDE_DIR})
+
     if(OpenCL_FOUND)
-      list(APPEND LevelZero_INCLUDE_DIRS ${OpenCL_INCLUDE_DIRS})
+        list(APPEND LevelZeroRuntime_INCLUDE_DIRS ${OpenCL_INCLUDE_DIRS})
     endif()
 
-    cmake_path(GET LevelZero_LIBRARY PARENT_PATH LevelZero_LIBRARIES_PATH)
-    set(LevelZero_LIBRARIES_DIR ${LevelZero_LIBRARIES_PATH})
-
-    if(NOT TARGET LevelZero::LevelZero)
-      add_library(LevelZero::LevelZero INTERFACE IMPORTED)
-      set_target_properties(LevelZero::LevelZero
-        PROPERTIES INTERFACE_LINK_LIBRARIES "${LevelZero_LIBRARIES}"
-      )
-      set_target_properties(LevelZero::LevelZero
-        PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LevelZero_INCLUDE_DIRS}"
-      )
+    cmake_path(GET LevelZeroRuntime_LIBRARY PARENT_PATH LevelZeroRuntime_LIBRARIES_PATH)
+    set(LevelZeroRuntime_LIBRARIES_DIR ${LevelZeroRuntime_LIBRARIES_PATH})
+
+    if(NOT TARGET LevelZeroRuntime::LevelZeroRuntime)
+        add_library(LevelZeroRuntime::LevelZeroRuntime INTERFACE IMPORTED)
+        set_target_properties(LevelZeroRuntime::LevelZeroRuntime
+            PROPERTIES INTERFACE_LINK_LIBRARIES "${LevelZeroRuntime_LIBRARIES}"
+        )
+        set_target_properties(LevelZeroRuntime::LevelZeroRuntime
+            PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LevelZeroRuntime_INCLUDE_DIRS}"
+        )
     endif()
 endif()
 
 # Check if a specific version of Level Zero is required
-if(LevelZero_FIND_VERSION)
+if(LevelZeroRuntime_FIND_VERSION)
     get_l0_loader_version()
     set(VERSION_GT_FIND_VERSION FALSE)
     compare_versions(
-        ${LevelZero_VERSION}
-        ${LevelZero_FIND_VERSION}
+        ${LevelZeroRuntime_VERSION}
+        ${LevelZeroRuntime_FIND_VERSION}
         VERSION_GT_FIND_VERSION
     )
+
     if(${VERSION_GT_FIND_VERSION})
-        set(LevelZero_FOUND TRUE)
+        set(LevelZeroRuntime_FOUND TRUE)
     else()
-        set(LevelZero_FOUND FALSE)
+        set(LevelZeroRuntime_FOUND FALSE)
     endif()
 else()
-    set(LevelZero_FOUND TRUE)
+    set(LevelZeroRuntime_FOUND TRUE)
 endif()
 
-find_package_handle_standard_args(LevelZero
+find_package_handle_standard_args(LevelZeroRuntime
     REQUIRED_VARS
-        LevelZero_FOUND
-        LevelZero_INCLUDE_DIRS
-        LevelZero_LIBRARY
-        LevelZero_LIBRARIES_DIR
+    LevelZeroRuntime_FOUND
+    LevelZeroRuntime_INCLUDE_DIRS
+    LevelZeroRuntime_LIBRARY
+    LevelZeroRuntime_LIBRARIES_DIR
     HANDLE_COMPONENTS
 )
-mark_as_advanced(LevelZero_LIBRARY LevelZero_INCLUDE_DIRS)
+mark_as_advanced(LevelZeroRuntime_LIBRARY LevelZeroRuntime_INCLUDE_DIRS)
 
-if(LevelZero_FOUND)
-    find_package_message(LevelZero "Found LevelZero: ${LevelZero_LIBRARY}"
-        "(found version ${LevelZero_VERSION})"
+if(LevelZeroRuntime_FOUND)
+    find_package_message(LevelZeroRuntime "Found LevelZero: ${LevelZeroRuntime_LIBRARY}"
+        "(found version ${LevelZeroRuntime_VERSION})"
     )
 else()
-    find_package_message(LevelZero "Could not find LevelZero" "")
+    find_package_message(LevelZeroRuntime "Could not find LevelZero" "")
 endif()
diff --git a/mlir/lib/ExecutionEngine/CMakeLists.txt b/mlir/lib/ExecutionEngine/CMakeLists.txt
index dd2ac75b88798..06c879f082926 100644
--- a/mlir/lib/ExecutionEngine/CMakeLists.txt
+++ b/mlir/lib/ExecutionEngine/CMakeLists.txt
@@ -14,12 +14,13 @@ set(LLVM_OPTIONAL_SOURCES
   RunnerUtils.cpp
   OptUtils.cpp
   JitRunner.cpp
+  LevelZeroRuntimeWrappers.cpp
   SpirvCpuRuntimeWrappers.cpp
   SyclRuntimeWrappers.cpp
   VulkanRuntimeWrappers.cpp
   VulkanRuntime.cpp
   VulkanRuntime.h
-  )
+)
 
 # Use a separate library for OptUtils, to avoid pulling in the entire JIT and
 # codegen infrastructure. Unlike MLIRExecutionEngine, this is part of
@@ -45,7 +46,7 @@ add_mlir_library(MLIRExecutionEngineUtils
   IPO
   Passes
   TargetParser
-  )
+)
 
 if(NOT MLIR_ENABLE_EXECUTION_ENGINE)
   return()
@@ -53,12 +54,12 @@ endif()
 
 if(LLVM_USE_INTEL_JITEVENTS)
   set(LLVM_JIT_LISTENER_LIB
-      IntelJITEvents)
+    IntelJITEvents)
 endif(LLVM_USE_INTEL_JITEVENTS)
 
 if(LLVM_USE_PERF)
   set(LLVM_JIT_LISTENER_LIB
-      PerfJITEvents)
+    PerfJITEvents)
 endif(LLVM_USE_PERF)
 
 add_mlir_library(MLIRExecutionEngine
@@ -91,7 +92,7 @@ add_mlir_library(MLIRExecutionEngine
   IPO
   Passes
   ${LLVM_JIT_LISTENER_LIB}
-  )
+)
 
 mlir_target_link_libraries(MLIRExecutionEngine PUBLIC
   MLIRBuiltinToLLVMIRTranslation
@@ -100,9 +101,9 @@ mlir_target_link_libraries(MLIRExecutionEngine PUBLIC
   MLIRLLVMToLLVMIRTranslation
   MLIROpenMPToLLVMIRTranslation
   MLIRTargetLLVMIRExport
-  )
+)
 
-if(LLVM_BUILD_LLVM_DYLIB AND NOT (WIN32 OR MINGW OR CYGWIN)) # Does not build on windows currently, see #106859
+if(LLVM_BUILD_LLVM_DYLIB AND NOT(WIN32 OR MINGW OR CYGWIN)) # Does not build on windows currently, see #106859
   # Build a shared library for the execution engine. Some downstream projects
   # use this library to build their own CPU runners while preserving dynamic
   # linkage.
@@ -122,7 +123,7 @@ if(LLVM_BUILD_LLVM_DYLIB AND NOT (WIN32 OR MINGW OR CYGWIN)) # Does not build on
     LINK_LIBS PUBLIC
     LLVM
     MLIR
-    )
+  )
 endif()
 
 get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
@@ -162,7 +163,7 @@ if(LLVM_ENABLE_PIC)
     Float16bits.cpp
 
     EXCLUDE_FROM_LIBMLIR
-    )
+  )
   set_property(TARGET mlir_float16_utils PROPERTY CXX_STANDARD 17)
   target_compile_definitions(mlir_float16_utils PRIVATE mlir_float16_utils_EXPORTS)
 
@@ -179,7 +180,7 @@ if(LLVM_ENABLE_PIC)
     mlir_float16_utils
     MLIRSparseTensorEnums
     MLIRSparseTensorRuntime
-    )
+  )
   set_property(TARGET mlir_c_runner_utils PROPERTY CXX_STANDARD 17)
   target_compile_definitions(mlir_c_runner_utils PRIVATE mlir_c_runner_utils_EXPORTS)
 
@@ -205,6 +206,7 @@ if(LLVM_ENABLE_PIC)
   )
   set_property(TARGET mlir_async_runtime PROPERTY CXX_VISIBILITY_PRESET hidden)
   target_compile_definitions(mlir_async_runtime PRIVATE mlir_async_runtime_EXPORTS)
+
   if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # Don't export symbols from link-time dependencies, these are internal
     # implementation details.
@@ -226,7 +228,8 @@ if(LLVM_ENABLE_PIC)
     # custom error message.
     include(CheckLanguage)
     check_language(CUDA)
-    if (CMAKE_CUDA_COMPILER)
+
+    if(CMAKE_CUDA_COMPILER)
       enable_language(CUDA)
     else()
       message(SEND_ERROR
@@ -290,13 +293,14 @@ if(LLVM_ENABLE_PIC)
 
   if(MLIR_ENABLE_ROCM_RUNNER)
     # Configure ROCm support.
-    if (NOT DEFINED ROCM_PATH)
-      if (NOT DEFINED ENV{ROCM_PATH})
+    if(NOT DEFINED ROCM_PATH)
+      if(NOT DEFINED ENV{ROCM_PATH})
         set(ROCM_PATH "/opt/rocm" CACHE PATH "Path to which ROCm has been installed")
       else()
         set(ROCM_PATH $ENV{ROCM_PATH} CACHE PATH "Path to which ROCm has been installed")
       endif()
     endif()
+
     # A lot of the ROCm CMake files expect to find their own dependencies in
     # CMAKE_PREFIX_PATH and don't respect PATHS or HINTS :( .
     # Therefore, temporarily add the ROCm path to CMAKE_PREFIX_PATH so we can
@@ -306,24 +310,28 @@ if(LLVM_ENABLE_PIC)
     find_package(hip REQUIRED)
     set(CMAKE_PREFIX_PATH "${REAL_CMAKE_PREFIX_PATH}")
 
-    if (NOT DEFINED ROCM_TEST_CHIPSET)
+    if(NOT DEFINED ROCM_TEST_CHIPSET)
       find_program(ROCM_AGENT_ENUMERATOR rocm_agent_enumerator "${ROCM_PATH}/bin" /usr/bin /usr/local/bin)
+
       if(ROCM_AGENT_ENUMERATOR)
-          execute_process(COMMAND "${ROCM_AGENT_ENUMERATOR}"
+        execute_process(COMMAND "${ROCM_AGENT_ENUMERATOR}"
           OUTPUT_VARIABLE AGENTS_STRING
           ERROR_VARIABLE AGENTS_STRING
           RESULT_VARIABLE AGENT_ENUMERATOR_RESULT)
       else()
         message(SEND_ERROR "Could not find rocm_agent_enumerator")
       endif()
-      if (NOT AGENT_ENUMERATOR_RESULT EQUAL 0)
+
+      if(NOT AGENT_ENUMERATOR_RESULT EQUAL 0)
         message(SEND_ERROR "Could not run rocm_agent_enumerator and ROCM_TEST_CHIPSET is not defined")
         set(AGENTS_STRING "")
       endif()
+
       string(STRIP AGENTS_STRING ${AGENTS_STRING})
       string(REPLACE "\n" ";" AGENTS_LIST ${AGENTS_STRING})
       list(FILTER AGENTS_LIST EXCLUDE REGEX "gfx000")
-      if (AGENTS_LIST STREQUAL "")
+
+      if(AGENTS_LIST STREQUAL "")
         message(SEND_ERROR "No non-CPU ROCm agents found on the system, and ROCM_TEST_CHIPSET is not defined")
       else()
         list(GET AGENTS_LIST 0 FIRST_AGENT)
@@ -342,27 +350,34 @@ if(LLVM_ENABLE_PIC)
     # Supress compiler warnings from HIP headers
     check_cxx_compiler_flag(-Wno-c++98-compat-extra-semi
       CXX_SUPPORTS_NO_CXX98_COMPAT_EXTRA_SEMI_FLAG)
-    if (CXX_SUPPORTS_CXX98_COMPAT_EXTRA_SEMI_FLAG)
+
+    if(CXX_SUPPORTS_CXX98_COMPAT_EXTRA_SEMI_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-c++98-compat-extra-semi")
     endif()
+
     check_cxx_compiler_flag(-Wno-return-type-c-linkage
-        CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
-    if (CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
+      CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
+
+    if(CXX_SUPPORTS_WNO_RETURN_TYPE_C_LINKAGE_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-return-type-c-linkage")
     endif()
+
     check_cxx_compiler_flag(-Wno-nested-anon-types
       CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
-    if (CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
+
+    if(CXX_SUPPORTS_WNO_NESTED_ANON_TYPES_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
         "-Wno-nested-anon-types")
     endif()
+
     check_cxx_compiler_flag(-Wno-gnu-anonymous-struct
       CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
-    if (CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
+
+    if(CXX_SUPPORTS_WNO_GNU_ANONYMOUS_STRUCT_FLAG)
       target_compile_options(mlir_rocm_runtime PRIVATE
-      "-Wno-gnu-anonymous-struct")
+        "-Wno-gnu-anonymous-struct")
     endif()
 
     set_property(TARGET mlir_rocm_runtime
@@ -381,9 +396,9 @@ if(LLVM_ENABLE_PIC)
       message(FATAL_ERROR "syclRuntime not found. Please set check oneapi installation and run setvars.sh.")
     endif()
 
-    find_package(LevelZero)
+    find_package(LevelZeroRuntime)
 
-    if(NOT LevelZero_FOUND)
+    if(NOT LevelZeroRuntime_FOUND)
       message(FATAL_ERROR "LevelZero not found. Please set LEVEL_ZERO_DIR.")
     endif()
 
@@ -395,18 +410,51 @@ if(LLVM_ENABLE_PIC)
     )
 
     check_cxx_compiler_flag("-frtti" CXX_HAS_FRTTI_FLAG)
+
     if(NOT CXX_HAS_FRTTI_FLAG)
       message(FATAL_ERROR "CXX compiler does not accept flag -frtti")
     endif()
-    target_compile_options (mlir_sycl_runtime PUBLIC -fexceptions -frtti)
+
+    target_compile_options(mlir_sycl_runtime PUBLIC -fexceptions -frtti)
 
     target_include_directories(mlir_sycl_runtime PRIVATE
       ${MLIR_INCLUDE_DIRS}
     )
 
-    target_link_libraries(mlir_sycl_runtime PRIVATE LevelZero::LevelZero SyclRuntime::SyclRuntime)
+    target_link_libraries(mlir_sycl_runtime PRIVATE LevelZeroRuntime::LevelZeroRuntime SyclRuntime::SyclRuntime)
+
+    set_property(TARGET mlir_sycl_runtime APPEND PROPERTY BUILD_RPATH "${LevelZeroRuntime_LIBRARIES_DIR}" "${SyclRuntime_LIBRARIES_DIR}")
+  endif()
+
+  if(MLIR_ENABLE_LEVEL_ZERO_RUNNER)
+    find_package(LevelZeroRuntime)
+
+    if(NOT LevelZeroRuntime_FOUND)
+      message(FATAL_ERROR "LevelZero not found. Please set LEVEL_ZERO_DIR.")
+    endif()
+
+    add_mlir_library(mlir_levelzero_runtime
+      SHARED
+      LevelZeroRuntimeWrappers.cpp
+
+      EXCLUDE_FROM_LIBMLIR
+    )
+
+    check_cxx_compiler_flag("-frtti" CXX_HAS_FRTTI_FLAG)
+
+    if(NOT CXX_HAS_FRTTI_FLAG)
+      message(FATAL_ERROR "CXX compiler does not accept flag -frtti")
+    endif()
+
+    target_compile_options(mlir_levelzero_runtime PUBLIC -fexceptions -frtti)
+
+    target_include_directories(mlir_levelzero_runtime PRIVATE
+      ${MLIR_INCLUDE_DIRS}
+    )
+
+    target_link_libraries(mlir_levelzero_runtime PRIVATE LevelZeroRuntime::LevelZeroRuntime)
 
-    set_property(TARGET mlir_sycl_runtime APPEND PROPERTY BUILD_RPATH "${LevelZero_LIBRARIES_DIR}" "${SyclRuntime_LIBRARIES_DIR}")
+    set_property(TARGET mlir_levelzero_runtime APPEND PROPERTY BUILD_RPATH "${LevelZeroRuntime_LIBRARIES_DIR}")
   endif()
 
   if(MLIR_ENABLE_SPIRV_CPU_RUNNER)
@@ -422,25 +470,26 @@ if(LLVM_ENABLE_PIC)
       mlir_spirv_cpu_runtime_EXPORTS)
   endif()
 
-  if (MLIR_ENABLE_VULKAN_RUNNER)
+  if(MLIR_ENABLE_VULKAN_RUNNER)
     find_package(Vulkan)
 
     # If Vulkan is not found try a path specified by VULKAN_SDK.
-    if (NOT Vulkan_FOUND)
-      if ("$ENV{VULKAN_SDK}" STREQUAL "")
+    if(NOT Vulkan_FOUND)
+      if("$ENV{VULKAN_SDK}" STREQUAL "")
         message(FATAL_ERROR "Vulkan not found through CMake; please provide "
-                            "VULKAN_SDK path as an environment variable")
+          "VULKAN_SDK path as an environment variable")
       endif()
 
       find_library(Vulkan_LIBRARY vulkan HINTS "$ENV{VULKAN_SDK}/lib" REQUIRED)
-      if (Vulkan_LIBRARY)
+
+      if(Vulkan_LIBRARY)
         set(Vulkan_FOUND ON)
         set(Vulkan_INCLUDE_DIR "$ENV{VULKAN_SDK}/include")
         message(STATUS "Found Vulkan: " ${Vulkan_LIBRARY})
       endif()
     endif()
 
-    if (NOT Vulkan_FOUND)
+    if(NOT Vulkan_FOUND)
       message(FATAL_ERROR "Cannot find Vulkan library")
     endif()
 
diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
new file mode 100644
index 0000000000000..70ac4761dc7fd
--- /dev/null
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -0,0 +1,491 @@
+//===- LevelZeroRuntimeWrappers.cpp - MLIR Level Zero (L0) wrapper library-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements wrappers around the Level Zero (L0) runtime library with C linkage
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Twine.h"
+
+#include <cassert>
+#include <deque>
+#include <exception>
+#include <functional>
+#include <iostream>
+#include <level_zero/ze_api.h>
+#include <limits>
+#include <unordered_set>
+#include <vector>
+
+namespace {
+
+template <typename F>
+auto catchAll(F &&func) {
+  try {
+    return func();
+  } catch (const std::exception &e) {
+    std::cerr << "An exception was thrown: " << e.what() << std::endl;
+    std::abort();
+  } catch (...) {
+    std::cerr << "An unknown exception was thrown." << std::endl;
+    std::abort();
+  }
+}
+
+#define L0_SAFE_CALL(call)                                                     \
+  {                                                                            \
+    ze_result_t status = (call);                                               \
+    if (status != ZE_RESULT_SUCCESS) {                                         \
+      std::cerr << "L0 error " << status << std::endl;                         \
+      std::abort();                                                            \
+    }                                                                          \
+  }
+
+} // namespace
+
+//===-------...
[truncated]

@mshahneo mshahneo requested review from mgorny and Hardcode84 July 28, 2025 20:50
@Hardcode84
Copy link
Contributor

@mshahneo we already have SYCLRuntimeWrappers, why not use those? Also, other folks from Intel are working on offload runtime upstream, can you please sync with them if their work can be reused? I would very much prefer not to multiply these terrible wrappers.

@mshahneo mshahneo requested review from rengolin and adam-smnk July 28, 2025 21:28
Copy link
Member

@rengolin rengolin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot comment on the L0 wrapper logic. @jian-lin @kurapov-peter please have a look.

@Hardcode84 the idea is to move away from the SYCL runner into a pure L0 runner so that we don't need to depend on the SYCL runtime when we're not using it. Once L0 is the default way to run on Intel GPUs, we should remove the SYCL runtime logic. SYCL can also use the L0 runtime.

@mshahneo, there are too many unrelated whitespace changes in this PR. Please roll-back any formatting change that you have done and only keep the actual logic changes in this PR. Thanks!


if(LLVM_BUILD_LLVM_DYLIB AND NOT (WIN32 OR MINGW OR CYGWIN)) # Does not build on windows currently, see #106859
if(LLVM_BUILD_LLVM_DYLIB AND NOT(WIN32 OR MINGW OR CYGWIN)) # Does not build on windows currently, see #106859
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid spurious whitespace changes. These increase greatly the amount of code to review when they should not have changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Renato (@rengolin ),

I apologize for this, I had a CMake formatter in my vscode which did auto-formatting :(.
Should I remove the formatted version?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least the parts that do not change otherwise. I'd recommend you disable auto-format in CMake files and only run on the lines you change and surrounding code. (Most editors can select code and run the formatter on the selection).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed now. Thanks, @rengolin :).

@Garra1980
Copy link

Cc @silee2 also

@adam-smnk adam-smnk requested a review from silee2 July 29, 2025 08:31
@fabianmcg
Copy link
Contributor

fabianmcg commented Jul 29, 2025

High-level comment, what's the difference between https://github.com/llvm/llvm-project/blob/main/mlir/lib/ExecutionEngine/SyclRuntimeWrappers.cpp and this?

Can't we get a single runtime wrapper?

Edit:

I just saw the above comments. Can the sycl wrappers be removed in this patch?

@mgorny mgorny removed their request for review July 29, 2025 11:28
@mshahneo
Copy link
Contributor Author

Can the sycl wrappers be removed in this patch?

Can we wait a bit? Once, the LevelZero wrappers are merged, we can remove the sycl wrappers. The transition would be easier in my opinion.

&immCmdListCompute));
}
void cleanup() {
L0_SAFE_CALL(zeCommandListDestroy(immCmdListCopy));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use std::unique_ptr with custom deleters for L0 handles instead of doing all the lifetime management manually

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, and if you leave it as is, there's potential for inconsistent states (e.g., user called cleanup, then getDefaultContext and got an invalid context handle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @Hardcode84 , @kurapov-peter!
Changed to std::unique_ptr

}

void enqueueOp(
std::function<void(ze_event_handle_t, uint32_t, ze_event_handle_t *)>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::function objects can be expensive to create, use llvm::function_ref or templated functor

template<typenamr F>
enqueueOp(F&& op)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done.

}

extern "C" void mgpuStreamDestroy(StreamWrapper *stream) {
if (stream)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete nullptr is allowed, this check is redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


extern "C" void mgpuStreamWaitEvent(StreamWrapper *stream,
ze_event_handle_t event) {
assert(stream && event);
Copy link
Contributor

@Hardcode84 Hardcode84 Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert(stream && "Invalid stream");
assert(event && "Invalid event");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

void mgpuMemset(void *dst, PATTERN_TYPE value, size_t count,
StreamWrapper *stream) {
auto listType =
getRtContext().copyEngineMaxMemoryFillPatternSize >= sizeof(PATTERN_TYPE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call getRtContext() once and take the reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@rengolin
Copy link
Member

Can the sycl wrappers be removed in this patch?

Can we wait a bit? Once, the LevelZero wrappers are merged, we can remove the sycl wrappers. The transition would be easier in my opinion.

Yeah, it's a matter of moving testing infrastructure. Having both at the same time while all CI moves to the new one allows them to fix any remaining problems in the LevelZero runner before completely removing the SYCL runner.

And having two is high cost, so removing them as soon as possible (after CI is green on L0) is the intention here.

@Jianhui-Li
Copy link
Contributor

I cannot comment on the L0 wrapper logic. @jian-lin @kurapov-peter please have a look.

@Hardcode84 the idea is to move away from the SYCL runner into a pure L0 runner so that we don't need to depend on the SYCL runtime when we're not using it. Once L0 is the default way to run on Intel GPUs, we should remove the SYCL runtime logic. SYCL can also use the L0 runtime.

The L0 runner removes the dependencies on SYCL runtime and also header files shipped together with SYCL compiler, so it simplifies MLIR development on Intel GPU.

SYCL runtime is not only for Intel GPU. It targets all SYCL compatible devices. We are open to remove it if there are no interests from the MLIR community to use it.

@fabianmcg
Copy link
Contributor

SYCL runtime is not only for Intel GPU. It targets all SYCL compatible devices. We are open to remove it if there are no interests from the MLIR community to use it.

It would be worth asking, because I don't think anyone would be using them other than for Intel GPUs (I might be wrong), so likely worth removing.

But okay, let's wait to the next patch to remove.


thread_local static bool isDriverInitialised{false};
if (isDriverInitialised)
return drivers[idx];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to compare idx with the size of drivers? I mean, what if the driver is initialized but the drivercount is less than idx requested?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, thanks for the catch, fixed now.

const int32_t devIdx = 0) {
thread_local static ze_device_handle_t l0Device;
thread_local static int32_t currDevIdx{-1};
if (devIdx == currDevIdx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that user give different driverIdx but same devIdx? In this case, they get same l0Device?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a static "current" device doesn't make too much sense. Also, if the API accepts the device ID, why is it called default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

};

// L0 only supports pre-determined sizes of event pools,
// implement a rt data struct to avoid running out of events.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rt-> runtime

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

eventPools.push_back(nullptr);
L0_SAFE_CALL(zeEventPoolCreate(rtCtx->context, &eventPoolDesc, 1,
&rtCtx->device, &eventPools.back()));
currentEventsLimit += numEvents;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the number of even and event pool surpass the "pre-determined sizes of event pools" supported by L0?

Copy link
Contributor

@akroviakov akroviakov Jul 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supported by L0

The line "pre-determined sizes of event pools" means that L0 only supports statically-sized event pools, rather than L0 itself has explicit limits on a pool size or event count. Maybe that line could use a better phrasing.

What happens if the number of even and event pool surpass ... supported by L0

As stated above, L0 does not expose the limits (at least not in the doc).
The logic for events is the following:

  1. Allocate an initial event pool of size X
  2. Take events from it (and release back once "synced"), so that events are recycled.
  3. If all of the events are taken - allocate a new pool. The structures for available/taken events consider events from all pools.

Yes, the dynamic pool de-allocation (once all of its events are freed) is not supported. Would it be useful to have it?
Events are recycled, pools can only be allocated. The assumption is that any adequate usage would not require too many events from L0 at the same time (as mentioned above, the limit is not even mentioned in the doc itself).
I agree that we could self-impose one for convenience to avoid possible undefined behavior. What would you consider a sensible limit on the total number of events for this wrapper?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a limit could be useful - just to be defensive in case something goes wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added maxEventsCount which is set to 32K events. It should be enough.

}

~DynamicEventPool() {
assert(!takenEvents.size());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider using std::unique_ptr with custom deletes, so you don't have to assert these conditions. Same comments as above https://github.com/llvm/llvm-project/pull/151038/files#r2240787050

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @Jianhui-Li , using std::unique_ptr now.

Fix Cmake whitespace isseu.
NAMES level_zero/ze_api.h
)

find_library(LevelZero_LIBRARY
find_library(LevelZeroRuntime_LIBRARY
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this renaming feels unnecessary

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it to make it consistent with SYCL. Since it's a one time thing, planning to keep it if no opposition.

endif()

if(MLIR_ENABLE_LEVELZERO_RUNNER)
find_package(LevelZeroRuntime)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this duplicating the search above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks, fixed it now.

check_cxx_compiler_flag("-frtti" CXX_HAS_FRTTI_FLAG)

if(NOT CXX_HAS_FRTTI_FLAG)
message(FATAL_ERROR "CXX compiler does not accept flag -frtti")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, why is having rtti a hard requirement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this was following the SYCL runtime. It's not a requirement for L0, removed.

{ \
ze_result_t status = (call); \
if (status != ZE_RESULT_SUCCESS) { \
std::cerr << "L0 error " << status << std::endl; \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend adding some descriptive error instead of a plain status. It dramatically improves the user experience. There's a L0 query for the status error string if you want to keep it concise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

const int32_t devIdx = 0) {
thread_local static ze_device_handle_t l0Device;
thread_local static int32_t currDevIdx{-1};
if (devIdx == currDevIdx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a static "current" device doesn't make too much sense. Also, if the API accepts the device ID, why is it called default?

&immCmdListCompute));
}
void cleanup() {
L0_SAFE_CALL(zeCommandListDestroy(immCmdListCopy));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, and if you leave it as is, there's potential for inconsistent states (e.g., user called cleanup, then getDefaultContext and got an invalid context handle.

ze_event_pool_desc_t eventPoolDesc = {};
eventPoolDesc.flags = ZE_EVENT_POOL_FLAG_HOST_VISIBLE;
eventPoolDesc.count = numEvents;
eventPools.push_back(nullptr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks a bit confusing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

mshahneo added 5 commits July 30, 2025 16:39
Add L0 error message.
Find levelzero only once if SYCL or LEVELZERO runner is enabled.
Add linebreak for large lines.
Rename test directory to 'LevelZero'.
Revert formatting added to Cmake File.

extern "C" void mgpuEventSynchronize(ze_event_handle_t event) {
L0_SAFE_CALL(
zeEventHostSynchronize(event, std::numeric_limits<uint64_t>::max()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to set a limit here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Jianhui-Li ,
std::numeric_limits<uint64_t>::max() has a special meaning for this function. It means the function will not return until complete or device is lost. (https://oneapi-src.github.io/level-zero-spec/level-zero/latest/core/api.html#zeeventhostsynchronize).

using UniqueZeCommandList =
std::unique_ptr<std::remove_pointer<ze_command_list_handle_t>::type,
ZeCommandListDeleter>;
struct L0RtContext {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider L0RtContextWrapper?to differentiate with the L0 context returned from zeContextCreate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, Changed the name to L0RTContextWrapper.

Limit the maximum no. of events.
Modify getContext to be a bit more generic.
Copy link
Contributor

@Jianhui-Li Jianhui-Li left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Remove unused code from CMake file.
@mshahneo
Copy link
Contributor Author

mshahneo commented Aug 5, 2025

@joker-eph , @rengolin , @fabianmcg , @kurapov-peter, any more comments?

@joker-eph
Copy link
Collaborator

Nothing from me, thanks for checking!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.