[Mlir-commits] [mlir] [mlir][gpu] Extend `mgpumoduleLoadJIT` API to add assemblySize parameter (PR #189429)

Md Abdullah Shahneous Bari llvmlistbot at llvm.org
Fri Apr 3 10:19:41 PDT 2026


https://github.com/mshahneo updated https://github.com/llvm/llvm-project/pull/189429

>From e265fe669c8ad4ad827b02b58cbc938d94154208 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Mon, 30 Mar 2026 16:37:45 +0000
Subject: [PATCH 1/8] [mlir][gpu] Extend `mgpumoduleLoadJIT` API to add
 assemblySize parameter

When JITing SPIR-V using LevelZero API, it expects the length
of the string since passed input data is a `void *`.
Problem is, getting the length of the string is not possible using
something like `strlen(reinterpret_cast<char *>(data))` in
`mgpuModuleLoadJIT` implementation. Becasuse the SPIR-V binary
contains null bytes (i.e., the data is binary SPIR-V,
not null-terminated text).

As a result we need to pass the `assmeblySize` via the
`mgpuModuleLoadJIT(void* data, int optLevel, size_t assmeblySize)`.
---
 .../ExecutionEngine/CudaRuntimeWrappers.cpp   |  4 +--
 .../LevelZeroRuntimeWrappers.cpp              |  6 ++--
 .../ExecutionEngine/RocmRuntimeWrappers.cpp   |  3 +-
 .../Target/LLVMIR/Dialect/GPU/CMakeLists.txt  |  1 +
 .../LLVMIR/Dialect/GPU/SelectObjectAttr.cpp   | 30 +++++++++++++++----
 5 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/mlir/lib/ExecutionEngine/CudaRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/CudaRuntimeWrappers.cpp
index f203363e16ea2..6307e0b59f3d2 100644
--- a/mlir/lib/ExecutionEngine/CudaRuntimeWrappers.cpp
+++ b/mlir/lib/ExecutionEngine/CudaRuntimeWrappers.cpp
@@ -124,8 +124,8 @@ mgpuModuleLoad(void *data, size_t /*gpuBlobSize*/) {
   return module;
 }
 
-extern "C" MLIR_CUDA_WRAPPERS_EXPORT CUmodule mgpuModuleLoadJIT(void *data,
-                                                                int optLevel) {
+extern "C" MLIR_CUDA_WRAPPERS_EXPORT CUmodule
+mgpuModuleLoadJIT(void *data, int optLevel, size_t /*assmeblySize*/) {
   ScopedContext scopedContext;
   CUmodule module = nullptr;
   char jitErrorBuffer[4096] = {0};
diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
index 01965da038820..75e997ead233d 100644
--- a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -520,10 +520,10 @@ extern "C" ze_module_handle_t mgpuModuleLoad(const void *data,
   return catchAll([&]() { return loadModule(data, gpuBlobSize); });
 }
 
-extern "C" ze_module_handle_t mgpuModuleLoadJIT(void *data, int optLevel) {
+extern "C" ze_module_handle_t mgpuModuleLoadJIT(void *data, int optLevel,
+                                                size_t assemblySize) {
   return catchAll([&]() {
-    return loadModule(data, strlen(reinterpret_cast<char *>(data)),
-                      ZE_MODULE_FORMAT_IL_SPIRV);
+    return loadModule(data, assemblySize, ZE_MODULE_FORMAT_IL_SPIRV);
   });
 }
 
diff --git a/mlir/lib/ExecutionEngine/RocmRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/RocmRuntimeWrappers.cpp
index b984149ca6dea..e729e4f9fca9d 100644
--- a/mlir/lib/ExecutionEngine/RocmRuntimeWrappers.cpp
+++ b/mlir/lib/ExecutionEngine/RocmRuntimeWrappers.cpp
@@ -38,7 +38,8 @@ extern "C" hipModule_t mgpuModuleLoad(void *data, size_t /*gpuBlobSize*/) {
   return module;
 }
 
-extern "C" hipModule_t mgpuModuleLoadJIT(void *data, int optLevel) {
+extern "C" hipModule_t mgpuModuleLoadJIT(void *data, int optLevel,
+                                         size_t /*assmeblySize*/) {
   assert(false && "This function is not available in HIP.");
   return nullptr;
 }
diff --git a/mlir/lib/Target/LLVMIR/Dialect/GPU/CMakeLists.txt b/mlir/lib/Target/LLVMIR/Dialect/GPU/CMakeLists.txt
index 11816ff5c2c1f..dd54dc10e1abe 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/GPU/CMakeLists.txt
+++ b/mlir/lib/Target/LLVMIR/Dialect/GPU/CMakeLists.txt
@@ -9,6 +9,7 @@ add_mlir_translation_library(MLIRGPUToLLVMIRTranslation
   MLIRIR
   MLIRGPUDialect
   MLIRLLVMDialect
+  MLIRXeVMTarget
   MLIRSupport
   MLIRTargetLLVMIRExport
   )
diff --git a/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp b/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
index 85fac86743a13..8e31a6e26fc4d 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
@@ -13,7 +13,10 @@
 
 #include "mlir/Dialect/GPU/IR/CompilationInterfaces.h"
 #include "mlir/Dialect/GPU/IR/GPUDialect.h"
+#include "mlir/Dialect/LLVMIR/XeVMDialect.h"
 
+#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
+#include "mlir/Target/LLVM/XeVM/Target.h"
 #include "mlir/Target/LLVMIR/Dialect/GPU/GPUToLLVMIRTranslation.h"
 #include "mlir/Target/LLVMIR/Export.h"
 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
@@ -98,8 +101,15 @@ static LogicalResult embedBinaryImpl(StringRef moduleName,
 
   // Embed the object as a global string.
   // Add null for assembly output for JIT paths that expect null-terminated
-  // strings.
-  bool addNull = (object.getFormat() == gpu::CompilationTarget::Assembly);
+  // strings. SPIR-V (for both XeVM and SPIR-V target) is passed as a binary
+  // blob and should not have a null terminator.
+  auto xevmTarget = dyn_cast<xevm::XeVMTargetAttr>(object.getTarget());
+  bool isXeVMSPIRV =
+      xevmTarget && xevmTarget.getTriple() == "spirv64-unknown-unknown";
+  bool isSPIRVTarget = isa<spirv::TargetEnvAttr>(object.getTarget());
+
+  bool addNull = !(isXeVMSPIRV || isSPIRVTarget) &&
+                 (object.getFormat() == gpu::CompilationTarget::Assembly);
   StringRef serializedStr = object.getObject().getValue();
   Constant *serializedCst =
       ConstantDataArray::getString(module.getContext(), serializedStr, addNull);
@@ -142,16 +152,24 @@ static LogicalResult embedBinaryImpl(StringRef moduleName,
   auto *loadBlock = BasicBlock::Create(module.getContext(), "entry", loadFn);
   builder.SetInsertPoint(loadBlock);
   Value *moduleObj = [&] {
+    Constant *binarySize =
+        ConstantInt::get(i64Ty, serializedStr.size() + (addNull ? 1 : 0));
     if (object.getFormat() == gpu::CompilationTarget::Assembly) {
       FunctionCallee moduleLoadFn = module.getOrInsertFunction(
-          "mgpuModuleLoadJIT", FunctionType::get(ptrTy, {ptrTy, i32Ty}, false));
+          "mgpuModuleLoadJIT", FunctionType::get(ptrTy,
+                                                 {
+                                                     ptrTy,
+                                                     i32Ty,
+                                                     i64Ty,
+                                                 },
+                                                 false));
+
       Constant *optValue = ConstantInt::get(i32Ty, optLevel);
-      return builder.CreateCall(moduleLoadFn, {serializedObj, optValue});
+      return builder.CreateCall(moduleLoadFn,
+                                {serializedObj, optValue, binarySize});
     }
     FunctionCallee moduleLoadFn = module.getOrInsertFunction(
         "mgpuModuleLoad", FunctionType::get(ptrTy, {ptrTy, i64Ty}, false));
-    Constant *binarySize =
-        ConstantInt::get(i64Ty, serializedStr.size() + (addNull ? 1 : 0));
     return builder.CreateCall(moduleLoadFn, {serializedObj, binarySize});
   }();
   builder.CreateStore(moduleObj, modulePtr);

>From 3eda8e1902cedecf43148614c60e326f0fb3bee9 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Mon, 30 Mar 2026 20:39:50 +0000
Subject: [PATCH 2/8] Address review comments.

Fix the layering issue.
---
 mlir/lib/Target/LLVM/XeVM/Target.cpp          | 13 +++++-
 .../Target/LLVMIR/Dialect/GPU/CMakeLists.txt  |  1 -
 .../LLVMIR/Dialect/GPU/SelectObjectAttr.cpp   | 42 ++++++++++---------
 mlir/lib/Target/SPIRV/Target.cpp              | 13 ++++++
 4 files changed, 47 insertions(+), 22 deletions(-)

diff --git a/mlir/lib/Target/LLVM/XeVM/Target.cpp b/mlir/lib/Target/LLVM/XeVM/Target.cpp
index 83eec5e9d5549..fd6c190f42ad2 100644
--- a/mlir/lib/Target/LLVM/XeVM/Target.cpp
+++ b/mlir/lib/Target/LLVM/XeVM/Target.cpp
@@ -475,10 +475,19 @@ XeVMTargetAttrImpl::createObject(Attribute attribute, Operation *module,
   gpu::CompilationTarget format = options.getCompilationTarget();
   auto xeTarget = cast<XeVMTargetAttr>(attribute);
   SmallVector<NamedAttribute, 2> properties;
-  if (format == gpu::CompilationTarget::Assembly)
+  if (format == gpu::CompilationTarget::Assembly) {
     properties.push_back(
         builder.getNamedAttr("O", builder.getI32IntegerAttr(xeTarget.getO())));
-
+    // If the object is serialized from a SPIR-V target, attach bool attribute
+    // "requires_null_terminator=false". Since, SPIR-V is a binary format it
+    // does not require a null terminator.
+    if (xeTarget.getTriple().starts_with("spirv")) {
+      properties.push_back(builder.getNamedAttr(
+          "requires_null_terminator",
+          builder.getBoolAttr(
+              false /*SPIR-V binary does not need null terminator*/)));
+    }
+  }
   DictionaryAttr objectProps;
   if (!properties.empty())
     objectProps = builder.getDictionaryAttr(properties);
diff --git a/mlir/lib/Target/LLVMIR/Dialect/GPU/CMakeLists.txt b/mlir/lib/Target/LLVMIR/Dialect/GPU/CMakeLists.txt
index dd54dc10e1abe..11816ff5c2c1f 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/GPU/CMakeLists.txt
+++ b/mlir/lib/Target/LLVMIR/Dialect/GPU/CMakeLists.txt
@@ -9,7 +9,6 @@ add_mlir_translation_library(MLIRGPUToLLVMIRTranslation
   MLIRIR
   MLIRGPUDialect
   MLIRLLVMDialect
-  MLIRXeVMTarget
   MLIRSupport
   MLIRTargetLLVMIRExport
   )
diff --git a/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp b/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
index 8e31a6e26fc4d..8650985adb5cb 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
@@ -99,17 +99,32 @@ namespace llvm {
 static LogicalResult embedBinaryImpl(StringRef moduleName,
                                      gpu::ObjectAttr object, Module &module) {
 
+  // Default JIT optimization level.
+  auto optLevel = APInt::getZero(32);
+  std::optional<StringRef> section = std::nullopt;
+  bool addNull = (object.getFormat() == gpu::CompilationTarget::Assembly);
+
+  if (DictionaryAttr objectProps = object.getProperties()) {
+    if (auto sectionAttr = dyn_cast_or_null<StringAttr>(
+            objectProps.get(gpu::elfSectionName))) {
+      section = sectionAttr.getValue();
+    }
+    // Check if there's an optimization level embedded in the object.
+    if (auto optAttr = dyn_cast_or_null<IntegerAttr>(objectProps.get("O")))
+      optLevel = optAttr.getValue();
+    // Check if there's an attribute indicating whether a null terminator is
+    // needed for the object.
+    if (auto nullTerminatorAttr = dyn_cast_or_null<BoolAttr>(
+            objectProps.get("requires_null_terminator"))) {
+      addNull = nullTerminatorAttr.getValue();
+    }
+  }
+
   // Embed the object as a global string.
   // Add null for assembly output for JIT paths that expect null-terminated
   // strings. SPIR-V (for both XeVM and SPIR-V target) is passed as a binary
   // blob and should not have a null terminator.
-  auto xevmTarget = dyn_cast<xevm::XeVMTargetAttr>(object.getTarget());
-  bool isXeVMSPIRV =
-      xevmTarget && xevmTarget.getTriple() == "spirv64-unknown-unknown";
-  bool isSPIRVTarget = isa<spirv::TargetEnvAttr>(object.getTarget());
 
-  bool addNull = !(isXeVMSPIRV || isSPIRVTarget) &&
-                 (object.getFormat() == gpu::CompilationTarget::Assembly);
   StringRef serializedStr = object.getObject().getValue();
   Constant *serializedCst =
       ConstantDataArray::getString(module.getContext(), serializedStr, addNull);
@@ -119,19 +134,8 @@ static LogicalResult embedBinaryImpl(StringRef moduleName,
                          serializedCst, moduleName + "_binary");
   serializedObj->setAlignment(MaybeAlign(8));
   serializedObj->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
-
-  // Default JIT optimization level.
-  auto optLevel = APInt::getZero(32);
-
-  if (DictionaryAttr objectProps = object.getProperties()) {
-    if (auto section = dyn_cast_or_null<StringAttr>(
-            objectProps.get(gpu::elfSectionName))) {
-      serializedObj->setSection(section.getValue());
-    }
-    // Check if there's an optimization level embedded in the object.
-    if (auto optAttr = dyn_cast_or_null<IntegerAttr>(objectProps.get("O")))
-      optLevel = optAttr.getValue();
-  }
+  if (section)
+    serializedObj->setSection(*section);
 
   IRBuilder<> builder(module.getContext());
   auto *i32Ty = builder.getInt32Ty();
diff --git a/mlir/lib/Target/SPIRV/Target.cpp b/mlir/lib/Target/SPIRV/Target.cpp
index be589b8292874..a07cf0debea1d 100644
--- a/mlir/lib/Target/SPIRV/Target.cpp
+++ b/mlir/lib/Target/SPIRV/Target.cpp
@@ -96,6 +96,19 @@ SPIRVTargetAttrImpl::createObject(Attribute attribute, Operation *module,
   gpu::CompilationTarget format = options.getCompilationTarget();
   DictionaryAttr objectProps;
   Builder builder(attribute.getContext());
+  // If compilation target is assembly, attach bool attribute
+  // "requires_null_terminator=false". This indicates, even if the compilation
+  // target is assembly, a null terminator is not required for SPIR-V. Because
+  // SPIR-V is a binary blob and does not require a null terminator.
+  if (format == gpu::CompilationTarget::Assembly) {
+    objectProps = DictionaryAttr::get(
+        attribute.getContext(),
+        {NamedAttribute(
+            "requires_null_terminator",
+            builder.getBoolAttr(
+                false /*SPIR-V binary does not need null terminator*/))});
+  }
+
   return builder.getAttr<gpu::ObjectAttr>(
       attribute, format,
       builder.getStringAttr(

>From 343aeee09a203e2a0400ee1f67b4cf9be27a7361 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Mon, 30 Mar 2026 20:44:06 +0000
Subject: [PATCH 3/8] Remove unncessary headers.

---
 mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp b/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
index 8650985adb5cb..35a359db07577 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
@@ -13,10 +13,6 @@
 
 #include "mlir/Dialect/GPU/IR/CompilationInterfaces.h"
 #include "mlir/Dialect/GPU/IR/GPUDialect.h"
-#include "mlir/Dialect/LLVMIR/XeVMDialect.h"
-
-#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
-#include "mlir/Target/LLVM/XeVM/Target.h"
 #include "mlir/Target/LLVMIR/Dialect/GPU/GPUToLLVMIRTranslation.h"
 #include "mlir/Target/LLVMIR/Export.h"
 #include "mlir/Target/LLVMIR/ModuleTranslation.h"

>From ce58ce0bed5c35eea141a2760508de0ad17f7c9d Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Tue, 31 Mar 2026 21:39:52 +0000
Subject: [PATCH 4/8] Address review comments.

Handle extra-null terminator in `moduleLoadJIT` implementation
in LevelZeroRuntimeWrapper.
---
 .../LevelZeroRuntimeWrappers.cpp              |  8 +++-
 mlir/lib/Target/LLVM/XeVM/Target.cpp          | 13 +-----
 .../LLVMIR/Dialect/GPU/SelectObjectAttr.cpp   | 41 +++++++------------
 mlir/lib/Target/SPIRV/Target.cpp              | 12 ------
 4 files changed, 24 insertions(+), 50 deletions(-)

diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
index 75e997ead233d..c4f799fe1ff0f 100644
--- a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -522,8 +522,14 @@ extern "C" ze_module_handle_t mgpuModuleLoad(const void *data,
 
 extern "C" ze_module_handle_t mgpuModuleLoadJIT(void *data, int optLevel,
                                                 size_t assemblySize) {
+  // Account for extra null terminator added in embedBinaryImpl.
+  // A null terminator is added during embedding binary for assembly format to
+  // support JIT paths that expect null-terminated strings. However, for SPIR-V
+  // binary format, the null terminator is not expected. So we need to subtract
+  // the null terminator when loading SPIR-V binary.
+  auto actualAssemblySize = assemblySize - 1;
   return catchAll([&]() {
-    return loadModule(data, assemblySize, ZE_MODULE_FORMAT_IL_SPIRV);
+    return loadModule(data, actualAssemblySize, ZE_MODULE_FORMAT_IL_SPIRV);
   });
 }
 
diff --git a/mlir/lib/Target/LLVM/XeVM/Target.cpp b/mlir/lib/Target/LLVM/XeVM/Target.cpp
index fd6c190f42ad2..83eec5e9d5549 100644
--- a/mlir/lib/Target/LLVM/XeVM/Target.cpp
+++ b/mlir/lib/Target/LLVM/XeVM/Target.cpp
@@ -475,19 +475,10 @@ XeVMTargetAttrImpl::createObject(Attribute attribute, Operation *module,
   gpu::CompilationTarget format = options.getCompilationTarget();
   auto xeTarget = cast<XeVMTargetAttr>(attribute);
   SmallVector<NamedAttribute, 2> properties;
-  if (format == gpu::CompilationTarget::Assembly) {
+  if (format == gpu::CompilationTarget::Assembly)
     properties.push_back(
         builder.getNamedAttr("O", builder.getI32IntegerAttr(xeTarget.getO())));
-    // If the object is serialized from a SPIR-V target, attach bool attribute
-    // "requires_null_terminator=false". Since, SPIR-V is a binary format it
-    // does not require a null terminator.
-    if (xeTarget.getTriple().starts_with("spirv")) {
-      properties.push_back(builder.getNamedAttr(
-          "requires_null_terminator",
-          builder.getBoolAttr(
-              false /*SPIR-V binary does not need null terminator*/)));
-    }
-  }
+
   DictionaryAttr objectProps;
   if (!properties.empty())
     objectProps = builder.getDictionaryAttr(properties);
diff --git a/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp b/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
index 35a359db07577..c25e9a3c36973 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/GPU/SelectObjectAttr.cpp
@@ -95,32 +95,10 @@ namespace llvm {
 static LogicalResult embedBinaryImpl(StringRef moduleName,
                                      gpu::ObjectAttr object, Module &module) {
 
-  // Default JIT optimization level.
-  auto optLevel = APInt::getZero(32);
-  std::optional<StringRef> section = std::nullopt;
-  bool addNull = (object.getFormat() == gpu::CompilationTarget::Assembly);
-
-  if (DictionaryAttr objectProps = object.getProperties()) {
-    if (auto sectionAttr = dyn_cast_or_null<StringAttr>(
-            objectProps.get(gpu::elfSectionName))) {
-      section = sectionAttr.getValue();
-    }
-    // Check if there's an optimization level embedded in the object.
-    if (auto optAttr = dyn_cast_or_null<IntegerAttr>(objectProps.get("O")))
-      optLevel = optAttr.getValue();
-    // Check if there's an attribute indicating whether a null terminator is
-    // needed for the object.
-    if (auto nullTerminatorAttr = dyn_cast_or_null<BoolAttr>(
-            objectProps.get("requires_null_terminator"))) {
-      addNull = nullTerminatorAttr.getValue();
-    }
-  }
-
   // Embed the object as a global string.
   // Add null for assembly output for JIT paths that expect null-terminated
-  // strings. SPIR-V (for both XeVM and SPIR-V target) is passed as a binary
-  // blob and should not have a null terminator.
-
+  // strings.
+  bool addNull = (object.getFormat() == gpu::CompilationTarget::Assembly);
   StringRef serializedStr = object.getObject().getValue();
   Constant *serializedCst =
       ConstantDataArray::getString(module.getContext(), serializedStr, addNull);
@@ -130,8 +108,19 @@ static LogicalResult embedBinaryImpl(StringRef moduleName,
                          serializedCst, moduleName + "_binary");
   serializedObj->setAlignment(MaybeAlign(8));
   serializedObj->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
-  if (section)
-    serializedObj->setSection(*section);
+
+  // Default JIT optimization level.
+  auto optLevel = APInt::getZero(32);
+
+  if (DictionaryAttr objectProps = object.getProperties()) {
+    if (auto section = dyn_cast_or_null<StringAttr>(
+            objectProps.get(gpu::elfSectionName))) {
+      serializedObj->setSection(section.getValue());
+    }
+    // Check if there's an optimization level embedded in the object.
+    if (auto optAttr = dyn_cast_or_null<IntegerAttr>(objectProps.get("O")))
+      optLevel = optAttr.getValue();
+  }
 
   IRBuilder<> builder(module.getContext());
   auto *i32Ty = builder.getInt32Ty();
diff --git a/mlir/lib/Target/SPIRV/Target.cpp b/mlir/lib/Target/SPIRV/Target.cpp
index a07cf0debea1d..80c28d8bd44dd 100644
--- a/mlir/lib/Target/SPIRV/Target.cpp
+++ b/mlir/lib/Target/SPIRV/Target.cpp
@@ -96,18 +96,6 @@ SPIRVTargetAttrImpl::createObject(Attribute attribute, Operation *module,
   gpu::CompilationTarget format = options.getCompilationTarget();
   DictionaryAttr objectProps;
   Builder builder(attribute.getContext());
-  // If compilation target is assembly, attach bool attribute
-  // "requires_null_terminator=false". This indicates, even if the compilation
-  // target is assembly, a null terminator is not required for SPIR-V. Because
-  // SPIR-V is a binary blob and does not require a null terminator.
-  if (format == gpu::CompilationTarget::Assembly) {
-    objectProps = DictionaryAttr::get(
-        attribute.getContext(),
-        {NamedAttribute(
-            "requires_null_terminator",
-            builder.getBoolAttr(
-                false /*SPIR-V binary does not need null terminator*/))});
-  }
 
   return builder.getAttr<gpu::ObjectAttr>(
       attribute, format,

>From e64db0f94ee963a32b4dfc938de3e0e77a7cc612 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Tue, 31 Mar 2026 21:42:59 +0000
Subject: [PATCH 5/8] Remove an extra space.

---
 mlir/lib/Target/SPIRV/Target.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/mlir/lib/Target/SPIRV/Target.cpp b/mlir/lib/Target/SPIRV/Target.cpp
index 80c28d8bd44dd..be589b8292874 100644
--- a/mlir/lib/Target/SPIRV/Target.cpp
+++ b/mlir/lib/Target/SPIRV/Target.cpp
@@ -96,7 +96,6 @@ SPIRVTargetAttrImpl::createObject(Attribute attribute, Operation *module,
   gpu::CompilationTarget format = options.getCompilationTarget();
   DictionaryAttr objectProps;
   Builder builder(attribute.getContext());
-
   return builder.getAttr<gpu::ObjectAttr>(
       attribute, format,
       builder.getStringAttr(

>From b8958137ff916f9e49fc2ad0542c3f56e1d95214 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Fri, 3 Apr 2026 16:25:05 +0000
Subject: [PATCH 6/8] Address review comments.

---
 mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
index c4f799fe1ff0f..a5efc6b337080 100644
--- a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -527,6 +527,9 @@ extern "C" ze_module_handle_t mgpuModuleLoadJIT(void *data, int optLevel,
   // support JIT paths that expect null-terminated strings. However, for SPIR-V
   // binary format, the null terminator is not expected. So we need to subtract
   // the null terminator when loading SPIR-V binary.
+  assert((assemblySize == 0 ||
+          reinterpret_cast<char *>(data)[assemblySize - 1] == 0) &&
+         "Expected null terminator at the end of the assembly string.");
   auto actualAssemblySize = assemblySize - 1;
   return catchAll([&]() {
     return loadModule(data, actualAssemblySize, ZE_MODULE_FORMAT_IL_SPIRV);

>From 102f4a8b818f76bdd65a8ecca43ab480cf03fefe Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Fri, 3 Apr 2026 16:27:59 +0000
Subject: [PATCH 7/8] Address review comments.

Add asserts on assemblySize.
---
 mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
index a5efc6b337080..f4f6afd958e6c 100644
--- a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -531,6 +531,8 @@ extern "C" ze_module_handle_t mgpuModuleLoadJIT(void *data, int optLevel,
           reinterpret_cast<char *>(data)[assemblySize - 1] == 0) &&
          "Expected null terminator at the end of the assembly string.");
   auto actualAssemblySize = assemblySize - 1;
+  assert(actualAssemblySize % 4 == 0 &&
+         "SPIR-V binary size must be a multiple of 4");
   return catchAll([&]() {
     return loadModule(data, actualAssemblySize, ZE_MODULE_FORMAT_IL_SPIRV);
   });

>From eee10ed854b613ee0a5e51f0568441cad5f9c842 Mon Sep 17 00:00:00 2001
From: "Shahneous Bari, Md Abdullah" <md.abdullah.shahneous.bari at intel.com>
Date: Fri, 3 Apr 2026 17:00:11 +0000
Subject: [PATCH 8/8] Address review comments.

---
 mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
index f4f6afd958e6c..91dab05b5a652 100644
--- a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -530,7 +530,7 @@ extern "C" ze_module_handle_t mgpuModuleLoadJIT(void *data, int optLevel,
   assert((assemblySize == 0 ||
           reinterpret_cast<char *>(data)[assemblySize - 1] == 0) &&
          "Expected null terminator at the end of the assembly string.");
-  auto actualAssemblySize = assemblySize - 1;
+  size_t actualAssemblySize = assemblySize - 1;
   assert(actualAssemblySize % 4 == 0 &&
          "SPIR-V binary size must be a multiple of 4");
   return catchAll([&]() {



More information about the Mlir-commits mailing list