This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new 551be33ed3 [Vulkan] Fix SPIR-V 1.4+ entry-point interfaces (#20028)
551be33ed3 is described below

commit 551be33ed3026ebde5bfe8399940c42c91373e96
Author: Václav Haisman <[email protected]>
AuthorDate: Mon Jul 20 10:57:07 2026 +0200

    [Vulkan] Fix SPIR-V 1.4+ entry-point interfaces (#20028)
    
    ## What changed
    
    This updates Vulkan code generation for targets requesting SPIR-V 1.4 or
    newer:
    
    - propagate `max_spirv_version` into `SPIRVSupport` and emit that
    version in the SPIR-V header;
    - track storage-buffer arguments, uniform buffers, push constants, and
    other module-scope variables;
    - include those variables in the `OpEntryPoint` interface list for
    SPIR-V 1.4+;
    - retain TVM's historical SPIR-V 1.0 output for older targets.
    
    ## Why this is necessary
    
    SPIR-V 1.4 changed the `OpEntryPoint` contract: every module-scope
    variable used by an entry point must be listed in its interface
    operands. TVM previously listed only built-in variables.
    
    TVM also always emitted a SPIR-V 1.0 header, even when the Vulkan target
    advertised SPIR-V 1.5. That caused validation to use the older rules and
    masked the incomplete interface list. Reassembling a generated
    storage-buffer kernel as SPIR-V 1.5 and validating it for Vulkan 1.2
    produced:
    
    ```text
    Interface variable id <4> is used by entry point 'main_kernel' id <2>,
    but is not listed as an interface
    %A_ptr = OpVariable ... StorageBuffer
    ```
    
    Local validation used the smallest useful kernel, `B[0] = A[0] + 1`. Its
    generated instructions were reassembled with `spirv-as --target-env
    spv1.5` and independently validated with `spirv-val --target-env
    vulkan1.2`. This external-tool validation is intentionally not part of
    the Python test suite.
    
    After the fix, its entry point contains both storage buffers:
    
    ```text
    OpEntryPoint GLCompute ... "main_kernel" %BuiltInLocalInvocationId %A_ptr 
%B_ptr
    ```
    
    ## Validation
    
    - Built TVM's Vulkan and LLVM-enabled compiler successfully.
    - Ran the complete repository lint suite:
    
      ```text
      pre-commit run --all-files
      All hooks passed
      ```
    
    - Ran the complete Vulkan codegen test module:
    
      ```text
      30 passed, 24 skipped
      ```
    
    - Confirmed `git diff --check` passes.
---
 src/backend/vulkan/codegen/ir_builder.cc    | 17 ++++++++++++++---
 src/backend/vulkan/codegen/ir_builder.h     |  6 ++++++
 src/backend/vulkan/codegen/spirv_support.cc |  1 +
 src/backend/vulkan/codegen/spirv_support.h  |  6 ++++++
 4 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/src/backend/vulkan/codegen/ir_builder.cc 
b/src/backend/vulkan/codegen/ir_builder.cc
index 9ceae01838..cac82e5242 100644
--- a/src/backend/vulkan/codegen/ir_builder.cc
+++ b/src/backend/vulkan/codegen/ir_builder.cc
@@ -43,9 +43,12 @@ void IRBuilder::InitHeader() {
   TVM_FFI_ICHECK_EQ(header_.size(), 0U);
   header_.push_back(spv::MagicNumber);
 
-  // Target SPIR-V version 1.0.  Additional functionality will be
-  // enabled through extensions.
-  header_.push_back(0x10000);
+  // Preserve TVM's historical SPIR-V 1.0 output for targets below 1.4.
+  // SPIR-V 1.4 changed the OpEntryPoint interface contract, so newer
+  // explicitly requested targets must carry their actual version.
+  header_.push_back(spirv_support_.max_spirv_version >= 0x00010400
+                        ? spirv_support_.max_spirv_version
+                        : 0x00010000);
 
   // generator: set to 0, unknown
   header_.push_back(0U);
@@ -269,6 +272,7 @@ Value IRBuilder::BufferArgument(const SType& value_type, 
uint32_t descriptor_set
   Value val = NewValue(ptr_type, kStructArrayPtr);
 
   ib_.Begin(spv::OpVariable).AddSeq(ptr_type, val, 
storage_class).Commit(&global_);
+  entry_point_interface_.push_back(val);
 
   this->DecorateBufferArgument(val, descriptor_set, binding);
   return val;
@@ -307,6 +311,7 @@ Value IRBuilder::DeclareStorageVariable(const 
std::vector<SType>& value_types,
   SType ptr_type = GetPointerType(struct_type, storage_class);
   Value val = NewValue(ptr_type, kind);
   ib_.Begin(spv::OpVariable).AddSeq(ptr_type, val, 
storage_class).Commit(&global_);
+  entry_point_interface_.push_back(val);
   return val;
 }
 
@@ -349,6 +354,11 @@ void IRBuilder::CommitKernelFunction(const Value& func, 
const std::string& name)
   for (auto& it : built_in_tbl_) {
     ib_.Add(it.second);
   }
+  if (spirv_support_.max_spirv_version >= 0x00010400) {
+    for (const auto& variable : entry_point_interface_) {
+      ib_.Add(variable);
+    }
+  }
   ib_.Commit(&entry_);
 }
 
@@ -379,6 +389,7 @@ Value IRBuilder::Allocate(const SType& value_type, uint32_t 
num_elems,
     ib_.Begin(spv::OpVariable).AddSeq(ptr_type, val, 
storage_class).Commit(&func_header_);
   } else {
     ib_.Begin(spv::OpVariable).AddSeq(ptr_type, val, 
storage_class).Commit(&global_);
+    entry_point_interface_.push_back(val);
   }
   return val;
 }
diff --git a/src/backend/vulkan/codegen/ir_builder.h 
b/src/backend/vulkan/codegen/ir_builder.h
index 9f2ea195c9..f642232a12 100644
--- a/src/backend/vulkan/codegen/ir_builder.h
+++ b/src/backend/vulkan/codegen/ir_builder.h
@@ -695,6 +695,12 @@ class IRBuilder {
    */
   std::unordered_map<spv::BuiltIn, Value> built_in_tbl_;
 
+  /*! \brief Module-scope variables used by the entry point.
+   *
+   * SPIR-V 1.4 and later require all used global variables in OpEntryPoint.
+   */
+  std::vector<Value> entry_point_interface_;
+
   /*! \brief The cached values for built-in values
    *
    *  Maps from a tuple of (spv::BuiltIn enum, index) to the value
diff --git a/src/backend/vulkan/codegen/spirv_support.cc 
b/src/backend/vulkan/codegen/spirv_support.cc
index 54a648ac7c..448be5ea94 100644
--- a/src/backend/vulkan/codegen/spirv_support.cc
+++ b/src/backend/vulkan/codegen/spirv_support.cc
@@ -37,6 +37,7 @@ SPIRVSupport::SPIRVSupport(tvm::Target target) {
       << "Unsupported device type for SPIRV codegen:" << device_type;
 
   vulkan_api_version = 
target->GetAttr<int64_t>("vulkan_api_version").value_or(vulkan_api_version);
+  max_spirv_version = 
target->GetAttr<int64_t>("max_spirv_version").value_or(max_spirv_version);
   supported_subgroup_operations = 
target->GetAttr<int64_t>("supported_subgroup_operations")
                                       .value_or(supported_subgroup_operations);
   max_push_constants_size =
diff --git a/src/backend/vulkan/codegen/spirv_support.h 
b/src/backend/vulkan/codegen/spirv_support.h
index f914f4d0d8..adce2f4ab6 100644
--- a/src/backend/vulkan/codegen/spirv_support.h
+++ b/src/backend/vulkan/codegen/spirv_support.h
@@ -51,6 +51,12 @@ struct SPIRVSupport {
    */
   uint32_t vulkan_api_version{VK_MAKE_VERSION(1, 0, 0)};
 
+  /*! \brief The maximum SPIR-V version requested by the target.
+   *
+   * Encoded using the SPIR-V header version word (for example, 0x00010500).
+   */
+  uint32_t max_spirv_version{0x00010000};
+
   /*!
    * \brief The supported subgroup operations
    *

Reply via email to