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
*