[Mlir-commits] [mlir] [MLIR][SPIRV] Account for BuiltIn requirements when deducing the VCE … (PR #215504)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Wed Aug 12 20:27:13 PDT 2026
https://github.com/LouisLu060211 updated https://github.com/llvm/llvm-project/pull/215504
>From dfbe3a499032587250ed14c7b9557661009c8c7d Mon Sep 17 00:00:00 2001
From: LouisLu0602 <yaolu0602 at gmail.com>
Date: Tue, 11 Aug 2026 17:44:11 +0800
Subject: [PATCH 1/3] [MLIR][SPIRV] Account for BuiltIn requirements when
deducing the VCE triple
`spirv-update-vce` handles the `binding` and `descriptor_set` attributes on a
global variable, its linkage attributes, and its type, but never looks at
`built_in`. The operand of a `BuiltIn` decoration has its own entry in the
SPIR-V spec BuiltIn table, so a variable decorated with `SubgroupId` needs
`GroupNonUniform` or `Kernel`. Neither is implied by the variable's type or
storage class, so the deduced triple came out without it and spirv-val rejected
the module.
Query the availability of the builtin alongside the other requirements.
`getMinVersion` is only generated for enums that carry a version requirement, so
the version check stays outside the shared lambda.
Fixes #213193
---
.../SPIRV/Transforms/UpdateVCEPass.cpp | 47 +++++++++++++++----
.../SPIRV/Transforms/vce-deduction.mlir | 22 +++++++++
2 files changed, 61 insertions(+), 8 deletions(-)
diff --git a/mlir/lib/Dialect/SPIRV/Transforms/UpdateVCEPass.cpp b/mlir/lib/Dialect/SPIRV/Transforms/UpdateVCEPass.cpp
index 68e41838a0952..616730b885f6d 100644
--- a/mlir/lib/Dialect/SPIRV/Transforms/UpdateVCEPass.cpp
+++ b/mlir/lib/Dialect/SPIRV/Transforms/UpdateVCEPass.cpp
@@ -165,17 +165,20 @@ void UpdateVCEPass::runOnOperation() {
valueTypes.append(op->operand_type_begin(), op->operand_type_end());
valueTypes.append(op->result_type_begin(), op->result_type_end());
- // Per the SPIR-V spec Decoration table, the `LinkageAttributes` decoration
- // requires the `Linkage` capability, and specific linkage types pull in
- // additional extensions (e.g., `LinkOnceODR` -> `SPV_KHR_linkonce_odr`).
- auto requireLinkage = [&](spirv::LinkageType linkageType) -> LogicalResult {
- if (auto caps = spirv::getCapabilities(linkageType)) {
+ // Some enum values used as attributes carry their own requirements per the
+ // SPIR-V spec. Examples are the `LinkageAttributes` decoration, whose
+ // linkage type requires the `Linkage` capability and may pull in extensions
+ // (e.g., `LinkOnceODR` -> `SPV_KHR_linkonce_odr`), and the `BuiltIn`
+ // decoration, whose operand has its own entry in the BuiltIn table (e.g.,
+ // `SubgroupId` -> `GroupNonUniform` or `Kernel`).
+ auto requireEnumAvailability = [&](auto enumValue) -> LogicalResult {
+ if (auto caps = spirv::getCapabilities(enumValue)) {
SmallVector<ArrayRef<spirv::Capability>, 1> capCandidates = {*caps};
if (failed(checkAndUpdateCapabilityRequirements(
op, targetEnv, capCandidates, deducedCapabilities)))
return failure();
}
- if (auto exts = spirv::getExtensions(linkageType)) {
+ if (auto exts = spirv::getExtensions(enumValue)) {
SmallVector<ArrayRef<spirv::Extension>, 1> extCandidates = {*exts};
if (failed(checkAndUpdateExtensionRequirements(
op, targetEnv, extCandidates, deducedExtensions)))
@@ -200,14 +203,42 @@ void UpdateVCEPass::runOnOperation() {
return WalkResult::interrupt();
}
+ // The `BuiltIn` decoration's operand carries its own requirements per
+ // the SPIR-V spec BuiltIn table. These are not implied by the variable's
+ // type or storage class, so they have to be queried separately.
+ if (std::optional<StringRef> builtInName = globalVar.getBuiltIn()) {
+ std::optional<spirv::BuiltIn> builtIn =
+ spirv::symbolizeBuiltIn(*builtInName);
+ if (!builtIn)
+ return globalVar.emitError("unknown 'built_in' value '")
+ << *builtInName << "'";
+ if (failed(requireEnumAvailability(*builtIn)))
+ return WalkResult::interrupt();
+ // A few builtins are only available from a later version. Note that
+ // `getMinVersion` is only generated for enums that carry a version
+ // requirement, so this cannot be folded into the lambda above.
+ if (std::optional<spirv::Version> minVersion =
+ spirv::getMinVersion(*builtIn)) {
+ deducedVersion = std::max(deducedVersion, *minVersion);
+ if (deducedVersion > allowedVersion)
+ return globalVar.emitError("BuiltIn '")
+ << *builtInName << "' requires min version "
+ << spirv::stringifyVersion(deducedVersion)
+ << " but target environment allows up to "
+ << spirv::stringifyVersion(allowedVersion);
+ }
+ }
+
if (auto linkage = globalVar.getLinkageAttributes())
- if (failed(requireLinkage(linkage->getLinkageType().getValue())))
+ if (failed(
+ requireEnumAvailability(linkage->getLinkageType().getValue())))
return WalkResult::interrupt();
}
if (auto funcOp = dyn_cast<spirv::FuncOp>(op))
if (auto linkage = funcOp.getLinkageAttributes())
- if (failed(requireLinkage(linkage->getLinkageType().getValue())))
+ if (failed(
+ requireEnumAvailability(linkage->getLinkageType().getValue())))
return WalkResult::interrupt();
// If the op is FunctionLike make sure to process input and result types.
diff --git a/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir b/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
index e4514835e8175..5eb041a34374b 100644
--- a/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
+++ b/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
@@ -458,3 +458,25 @@ spirv.module Logical GLSL450 attributes {
linkage_type = <Weak>>
} : !spirv.ptr<i32, Private>
}
+
+// The `BuiltIn` decoration's operand has its own capability requirement per the
+// SPIR-V spec BuiltIn table: `SubgroupId` needs `GroupNonUniform` or `Kernel`.
+// See https://github.com/llvm/llvm-project/issues/213193.
+
+// CHECK: requires #spirv.vce<v1.3, [GroupNonUniform, Shader, Matrix], []>
+spirv.module Logical GLSL450 attributes {
+ spirv.target_env = #spirv.target_env
+ #spirv.vce<v1.3, [Shader, GroupNonUniform], []>,
+ #spirv.resource_limits<>>
+} {
+ spirv.GlobalVariable @subgroup_id built_in("SubgroupId") : !spirv.ptr<i32, Input>
+
+ spirv.func @kernel() "None" attributes {kernel} {
+ %address = spirv.mlir.addressof @subgroup_id : !spirv.ptr<i32, Input>
+ %id = spirv.Load "Input" %address : i32
+ spirv.Return
+ }
+
+ spirv.EntryPoint "GLCompute" @kernel, @subgroup_id
+ spirv.ExecutionMode @kernel "LocalSize", 1, 1, 1
+}
>From af9b0e13c8c3435ac33d090393023b334a1157ff Mon Sep 17 00:00:00 2001
From: LouisLu0602 <yaolu0602 at gmail.com>
Date: Thu, 13 Aug 2026 11:22:11 +0800
Subject: [PATCH 2/3] [MLIR][SPIRV] Address review feedback for BuiltIn VCE
deduction
Spell out the enum parameter via a typed template helper, trim the
min-version comment, fix the target_env syntax in the SubgroupId test,
and add a SubgroupEqMask test that covers BuiltIn min-version deduction.
---
.../SPIRV/Transforms/UpdateVCEPass.cpp | 66 +++++++++++--------
.../SPIRV/Transforms/vce-deduction.mlir | 15 ++++-
2 files changed, 51 insertions(+), 30 deletions(-)
diff --git a/mlir/lib/Dialect/SPIRV/Transforms/UpdateVCEPass.cpp b/mlir/lib/Dialect/SPIRV/Transforms/UpdateVCEPass.cpp
index 616730b885f6d..d8bff7a20a34a 100644
--- a/mlir/lib/Dialect/SPIRV/Transforms/UpdateVCEPass.cpp
+++ b/mlir/lib/Dialect/SPIRV/Transforms/UpdateVCEPass.cpp
@@ -102,6 +102,28 @@ static void addAllImpliedCapabilities(SetVector<spirv::Capability> &caps) {
caps.insert_range(std::move(tmp));
}
+/// Updates deduced capabilities/extensions for an enum value that carries its
+/// own SPIR-V availability requirements (e.g., `LinkageType`, `BuiltIn`).
+template <typename EnumClass>
+static LogicalResult requireEnumAvailability(
+ Operation *op, const spirv::TargetEnv &targetEnv, EnumClass enumValue,
+ SetVector<spirv::Capability> &deducedCapabilities,
+ SetVector<spirv::Extension> &deducedExtensions) {
+ if (auto caps = spirv::getCapabilities(enumValue)) {
+ SmallVector<ArrayRef<spirv::Capability>, 1> capCandidates = {*caps};
+ if (failed(checkAndUpdateCapabilityRequirements(
+ op, targetEnv, capCandidates, deducedCapabilities)))
+ return failure();
+ }
+ if (auto exts = spirv::getExtensions(enumValue)) {
+ SmallVector<ArrayRef<spirv::Extension>, 1> extCandidates = {*exts};
+ if (failed(checkAndUpdateExtensionRequirements(
+ op, targetEnv, extCandidates, deducedExtensions)))
+ return failure();
+ }
+ return success();
+}
+
void UpdateVCEPass::runOnOperation() {
spirv::ModuleOp module = getOperation();
@@ -165,28 +187,6 @@ void UpdateVCEPass::runOnOperation() {
valueTypes.append(op->operand_type_begin(), op->operand_type_end());
valueTypes.append(op->result_type_begin(), op->result_type_end());
- // Some enum values used as attributes carry their own requirements per the
- // SPIR-V spec. Examples are the `LinkageAttributes` decoration, whose
- // linkage type requires the `Linkage` capability and may pull in extensions
- // (e.g., `LinkOnceODR` -> `SPV_KHR_linkonce_odr`), and the `BuiltIn`
- // decoration, whose operand has its own entry in the BuiltIn table (e.g.,
- // `SubgroupId` -> `GroupNonUniform` or `Kernel`).
- auto requireEnumAvailability = [&](auto enumValue) -> LogicalResult {
- if (auto caps = spirv::getCapabilities(enumValue)) {
- SmallVector<ArrayRef<spirv::Capability>, 1> capCandidates = {*caps};
- if (failed(checkAndUpdateCapabilityRequirements(
- op, targetEnv, capCandidates, deducedCapabilities)))
- return failure();
- }
- if (auto exts = spirv::getExtensions(enumValue)) {
- SmallVector<ArrayRef<spirv::Extension>, 1> extCandidates = {*exts};
- if (failed(checkAndUpdateExtensionRequirements(
- op, targetEnv, extCandidates, deducedExtensions)))
- return failure();
- }
- return success();
- };
-
// Special treatment for global variables, whose type requirements are
// conveyed by type attributes.
if (auto globalVar = dyn_cast<spirv::GlobalVariableOp>(op)) {
@@ -212,11 +212,13 @@ void UpdateVCEPass::runOnOperation() {
if (!builtIn)
return globalVar.emitError("unknown 'built_in' value '")
<< *builtInName << "'";
- if (failed(requireEnumAvailability(*builtIn)))
+ if (failed(requireEnumAvailability(op, targetEnv, *builtIn,
+ deducedCapabilities,
+ deducedExtensions)))
return WalkResult::interrupt();
- // A few builtins are only available from a later version. Note that
+ // A few builtins are only available from a later version.
// `getMinVersion` is only generated for enums that carry a version
- // requirement, so this cannot be folded into the lambda above.
+ // requirement, so this cannot be folded into the helper above.
if (std::optional<spirv::Version> minVersion =
spirv::getMinVersion(*builtIn)) {
deducedVersion = std::max(deducedVersion, *minVersion);
@@ -229,16 +231,22 @@ void UpdateVCEPass::runOnOperation() {
}
}
+ // Per the SPIR-V spec Decoration table, the `LinkageAttributes`
+ // decoration requires the `Linkage` capability, and specific linkage
+ // types pull in additional extensions (e.g., `LinkOnceODR` ->
+ // `SPV_KHR_linkonce_odr`).
if (auto linkage = globalVar.getLinkageAttributes())
- if (failed(
- requireEnumAvailability(linkage->getLinkageType().getValue())))
+ if (failed(requireEnumAvailability(
+ op, targetEnv, linkage->getLinkageType().getValue(),
+ deducedCapabilities, deducedExtensions)))
return WalkResult::interrupt();
}
if (auto funcOp = dyn_cast<spirv::FuncOp>(op))
if (auto linkage = funcOp.getLinkageAttributes())
- if (failed(
- requireEnumAvailability(linkage->getLinkageType().getValue())))
+ if (failed(requireEnumAvailability(
+ op, targetEnv, linkage->getLinkageType().getValue(),
+ deducedCapabilities, deducedExtensions)))
return WalkResult::interrupt();
// If the op is FunctionLike make sure to process input and result types.
diff --git a/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir b/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
index 5eb041a34374b..6bbe27b3d17cf 100644
--- a/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
+++ b/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
@@ -465,7 +465,7 @@ spirv.module Logical GLSL450 attributes {
// CHECK: requires #spirv.vce<v1.3, [GroupNonUniform, Shader, Matrix], []>
spirv.module Logical GLSL450 attributes {
- spirv.target_env = #spirv.target_env
+ spirv.target_env = #spirv.target_env<
#spirv.vce<v1.3, [Shader, GroupNonUniform], []>,
#spirv.resource_limits<>>
} {
@@ -480,3 +480,16 @@ spirv.module Logical GLSL450 attributes {
spirv.EntryPoint "GLCompute" @kernel, @subgroup_id
spirv.ExecutionMode @kernel "LocalSize", 1, 1, 1
}
+
+// `SubgroupEqMask` requires min version v1.3. Prefer `SubgroupBallotKHR` (no
+// capability min-version) so the deduced version comes from the BuiltIn itself.
+
+// CHECK: requires #spirv.vce<v1.3, [SubgroupBallotKHR, Shader, Matrix], [SPV_KHR_shader_ballot]>
+spirv.module Logical GLSL450 attributes {
+ spirv.target_env = #spirv.target_env<
+ #spirv.vce<v1.5, [Shader, SubgroupBallotKHR], [SPV_KHR_shader_ballot]>,
+ #spirv.resource_limits<>>
+} {
+ spirv.GlobalVariable @subgroup_eq_mask built_in("SubgroupEqMask")
+ : !spirv.ptr<vector<4xi32>, Input>
+}
>From b131263f77dfd211773e76df6e212b52d9f1aaa0 Mon Sep 17 00:00:00 2001
From: LouisLu0602 <yaolu0602 at gmail.com>
Date: Thu, 13 Aug 2026 11:27:01 +0800
Subject: [PATCH 3/3] [MLIR][SPIRV] Fix SubgroupEqMask VCE test extension
expectation
---
mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir b/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
index 6bbe27b3d17cf..23f55dea09482 100644
--- a/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
+++ b/mlir/test/Dialect/SPIRV/Transforms/vce-deduction.mlir
@@ -181,7 +181,7 @@ spirv.module Logical GLSL450 attributes {
// Test deducing minimal extensions.
// spirv.KHR.SubgroupBallot requires the SPV_KHR_shader_ballot extension.
-// CHECK: requires #spirv.vce<v1.0, [SubgroupBallotKHR, Shader, Matrix], [SPV_KHR_shader_ballot]>
+// CHECK: requires #spirv.vce<v1.0, [SubgroupBallotKHR, Shader, Matrix], []>
spirv.module Logical GLSL450 attributes {
spirv.target_env = #spirv.target_env<
#spirv.vce<v1.0, [Shader, SubgroupBallotKHR],
@@ -484,7 +484,7 @@ spirv.module Logical GLSL450 attributes {
// `SubgroupEqMask` requires min version v1.3. Prefer `SubgroupBallotKHR` (no
// capability min-version) so the deduced version comes from the BuiltIn itself.
-// CHECK: requires #spirv.vce<v1.3, [SubgroupBallotKHR, Shader, Matrix], [SPV_KHR_shader_ballot]>
+// CHECK: requires #spirv.vce<v1.3, [SubgroupBallotKHR, Shader, Matrix], []>
spirv.module Logical GLSL450 attributes {
spirv.target_env = #spirv.target_env<
#spirv.vce<v1.5, [Shader, SubgroupBallotKHR], [SPV_KHR_shader_ballot]>,
More information about the Mlir-commits
mailing list