[Mlir-commits] [mlir] [mlir][SPIR-V] Verify ExecutionMode operand count and kind (PR #212828)
Arseniy Obolenskiy
llvmlistbot at llvm.org
Thu Jul 30 01:41:48 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/212828
>From 7ff17b4afd9850edff83bd76e48d2578822e21bc Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 29 Jul 2026 19:28:23 +0200
Subject: [PATCH] [mlir][SPIR-V] Verify ExecutionMode operand count and kind
Add a verifier for spirv.ExecutionMode enforcing the correct number of literal operands per execution mode and rejecting `<id>`-only modes and share the operand schema with spirv.ExecutionModeId
---
.../Dialect/SPIRV/IR/SPIRVStructureOps.td | 2 +-
mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp | 88 +++++++++--
mlir/test/Dialect/SPIRV/IR/structure-ops.mlir | 141 ++++++++++++++++++
3 files changed, 220 insertions(+), 11 deletions(-)
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td
index 6899fc2dabc70..43a0dbac17e66 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td
@@ -279,7 +279,7 @@ def SPIRV_ExecutionModeOp : SPIRV_Op<"ExecutionMode", [InModuleScope]> {
let results = (outs);
- let hasVerifier = 0;
+ let hasVerifier = 1;
let autogenSerialization = 0;
diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
index 99bc3913c56c6..fed2764080b01 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
@@ -929,6 +929,59 @@ LogicalResult spirv::EntryPointOp::verify() {
return success();
}
+//===----------------------------------------------------------------------===//
+// spirv.ExecutionMode / spirv.ExecutionModeId
+//===----------------------------------------------------------------------===//
+
+namespace {
+// Describes the extra operands a SPIR-V ExecutionMode expects: whether they
+// are <id> operands (only valid on spirv.ExecutionModeId) or literal integers
+// (only valid on spirv.ExecutionMode), and how many of them are required.
+struct ExecutionModeOperandSchema {
+ bool isIdOperand;
+ unsigned numOperands;
+};
+
+ExecutionModeOperandSchema
+getExecutionModeOperandSchema(spirv::ExecutionMode mode) {
+ switch (mode) {
+ case spirv::ExecutionMode::Invocations:
+ case spirv::ExecutionMode::OutputVertices:
+ case spirv::ExecutionMode::VecTypeHint:
+ case spirv::ExecutionMode::SubgroupSize:
+ case spirv::ExecutionMode::SubgroupsPerWorkgroup:
+ case spirv::ExecutionMode::DenormPreserve:
+ case spirv::ExecutionMode::DenormFlushToZero:
+ case spirv::ExecutionMode::SignedZeroInfNanPreserve:
+ case spirv::ExecutionMode::RoundingModeRTE:
+ case spirv::ExecutionMode::RoundingModeRTZ:
+ case spirv::ExecutionMode::OutputPrimitivesEXT:
+ case spirv::ExecutionMode::SharedLocalMemorySizeINTEL:
+ case spirv::ExecutionMode::RoundingModeRTPINTEL:
+ case spirv::ExecutionMode::RoundingModeRTNINTEL:
+ case spirv::ExecutionMode::FloatingPointModeALTINTEL:
+ case spirv::ExecutionMode::FloatingPointModeIEEEINTEL:
+ case spirv::ExecutionMode::MaxWorkDimINTEL:
+ case spirv::ExecutionMode::NumSIMDWorkitemsINTEL:
+ case spirv::ExecutionMode::SchedulerTargetFmaxMhzINTEL:
+ case spirv::ExecutionMode::StreamingInterfaceINTEL:
+ case spirv::ExecutionMode::NamedBarrierCountINTEL:
+ return {/*isIdOperand=*/false, /*numOperands=*/1};
+ case spirv::ExecutionMode::LocalSize:
+ case spirv::ExecutionMode::LocalSizeHint:
+ case spirv::ExecutionMode::MaxWorkgroupSizeINTEL:
+ return {/*isIdOperand=*/false, /*numOperands=*/3};
+ case spirv::ExecutionMode::SubgroupsPerWorkgroupId:
+ return {/*isIdOperand=*/true, /*numOperands=*/1};
+ case spirv::ExecutionMode::LocalSizeId:
+ case spirv::ExecutionMode::LocalSizeHintId:
+ return {/*isIdOperand=*/true, /*numOperands=*/3};
+ default:
+ return {/*isIdOperand=*/false, /*numOperands=*/0};
+ }
+}
+} // namespace
+
//===----------------------------------------------------------------------===//
// spirv.ExecutionMode
//===----------------------------------------------------------------------===//
@@ -977,6 +1030,23 @@ void spirv::ExecutionModeOp::print(OpAsmPrinter &printer) {
printer << ", " << llvm::interleaved(values.getAsValueRange<IntegerAttr>());
}
+LogicalResult spirv::ExecutionModeOp::verify() {
+ ExecutionModeOperandSchema schema =
+ getExecutionModeOperandSchema(getExecutionMode());
+
+ if (schema.isIdOperand)
+ return emitOpError("expected ExecutionMode that takes extra operands "
+ "that are not <id> operands, got: ")
+ << stringifyExecutionMode(getExecutionMode());
+
+ if (getValues().size() != schema.numOperands)
+ return emitOpError("expected ")
+ << schema.numOperands << " value operand(s), got "
+ << getValues().size();
+
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// spirv.ExecutionModeId
//===----------------------------------------------------------------------===//
@@ -1018,20 +1088,18 @@ void spirv::ExecutionModeIdOp::print(OpAsmPrinter &printer) {
}
LogicalResult spirv::ExecutionModeIdOp::verify() {
- // Valid as of SPIRV 1.6
- switch (getExecutionMode()) {
- case ExecutionMode::SubgroupsPerWorkgroupId:
- case ExecutionMode::LocalSizeId:
- case ExecutionMode::LocalSizeHintId:
- break;
- default:
+ ExecutionModeOperandSchema schema =
+ getExecutionModeOperandSchema(getExecutionMode());
+
+ if (!schema.isIdOperand)
return emitOpError("expected ExecutionMode that takes extra operands that "
"are <id> operands, got: ")
<< stringifyExecutionMode(getExecutionMode());
- }
- if (getValues().empty())
- return emitOpError("expected at least one value operand");
+ if (getValues().size() != schema.numOperands)
+ return emitOpError("expected ")
+ << schema.numOperands << " value operand(s), got "
+ << getValues().size();
for (Attribute value : getValues()) {
auto valueSymbol = dyn_cast<FlatSymbolRefAttr>(value);
diff --git a/mlir/test/Dialect/SPIRV/IR/structure-ops.mlir b/mlir/test/Dialect/SPIRV/IR/structure-ops.mlir
index 37a9e97106de9..4acaa417501ac 100644
--- a/mlir/test/Dialect/SPIRV/IR/structure-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/structure-ops.mlir
@@ -342,6 +342,65 @@ spirv.module Logical GLSL450 {
spirv.ExecutionMode @do_nothing "LocalSizeHint", 3, 4, 5
}
+spirv.module Logical GLSL450 {
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // CHECK: spirv.ExecutionMode {{@.*}} "MaxWorkgroupSizeINTEL", 4, 4, 4
+ spirv.ExecutionMode @do_nothing "MaxWorkgroupSizeINTEL", 4, 4, 4
+}
+
+// Exercise every ExecutionMode that takes exactly one literal operand.
+spirv.module Logical GLSL450 {
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // CHECK: spirv.ExecutionMode {{@.*}} "Invocations", 4
+ spirv.ExecutionMode @do_nothing "Invocations", 4
+ // CHECK: spirv.ExecutionMode {{@.*}} "OutputVertices", 4
+ spirv.ExecutionMode @do_nothing "OutputVertices", 4
+ // CHECK: spirv.ExecutionMode {{@.*}} "VecTypeHint", 4
+ spirv.ExecutionMode @do_nothing "VecTypeHint", 4
+ // CHECK: spirv.ExecutionMode {{@.*}} "SubgroupSize", 4
+ spirv.ExecutionMode @do_nothing "SubgroupSize", 4
+ // CHECK: spirv.ExecutionMode {{@.*}} "SubgroupsPerWorkgroup", 4
+ spirv.ExecutionMode @do_nothing "SubgroupsPerWorkgroup", 4
+ // CHECK: spirv.ExecutionMode {{@.*}} "DenormPreserve", 32
+ spirv.ExecutionMode @do_nothing "DenormPreserve", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "DenormFlushToZero", 32
+ spirv.ExecutionMode @do_nothing "DenormFlushToZero", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "SignedZeroInfNanPreserve", 32
+ spirv.ExecutionMode @do_nothing "SignedZeroInfNanPreserve", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "RoundingModeRTE", 32
+ spirv.ExecutionMode @do_nothing "RoundingModeRTE", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "RoundingModeRTZ", 32
+ spirv.ExecutionMode @do_nothing "RoundingModeRTZ", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "OutputPrimitivesEXT", 4
+ spirv.ExecutionMode @do_nothing "OutputPrimitivesEXT", 4
+ // CHECK: spirv.ExecutionMode {{@.*}} "SharedLocalMemorySizeINTEL", 4
+ spirv.ExecutionMode @do_nothing "SharedLocalMemorySizeINTEL", 4
+ // CHECK: spirv.ExecutionMode {{@.*}} "RoundingModeRTPINTEL", 32
+ spirv.ExecutionMode @do_nothing "RoundingModeRTPINTEL", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "RoundingModeRTNINTEL", 32
+ spirv.ExecutionMode @do_nothing "RoundingModeRTNINTEL", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "FloatingPointModeALTINTEL", 32
+ spirv.ExecutionMode @do_nothing "FloatingPointModeALTINTEL", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "FloatingPointModeIEEEINTEL", 32
+ spirv.ExecutionMode @do_nothing "FloatingPointModeIEEEINTEL", 32
+ // CHECK: spirv.ExecutionMode {{@.*}} "MaxWorkDimINTEL", 3
+ spirv.ExecutionMode @do_nothing "MaxWorkDimINTEL", 3
+ // CHECK: spirv.ExecutionMode {{@.*}} "NumSIMDWorkitemsINTEL", 4
+ spirv.ExecutionMode @do_nothing "NumSIMDWorkitemsINTEL", 4
+ // CHECK: spirv.ExecutionMode {{@.*}} "SchedulerTargetFmaxMhzINTEL", 400
+ spirv.ExecutionMode @do_nothing "SchedulerTargetFmaxMhzINTEL", 400
+ // CHECK: spirv.ExecutionMode {{@.*}} "StreamingInterfaceINTEL", 0
+ spirv.ExecutionMode @do_nothing "StreamingInterfaceINTEL", 0
+ // CHECK: spirv.ExecutionMode {{@.*}} "NamedBarrierCountINTEL", 4
+ spirv.ExecutionMode @do_nothing "NamedBarrierCountINTEL", 4
+}
+
// -----
spirv.module Logical GLSL450 {
@@ -355,6 +414,50 @@ spirv.module Logical GLSL450 {
// -----
+spirv.module Logical GLSL450 {
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // expected-error @+1 {{'spirv.ExecutionMode' op expected ExecutionMode that takes extra operands that are not <id> operands, got: LocalSizeId}}
+ spirv.ExecutionMode @do_nothing "LocalSizeId", 3, 4, 5
+}
+
+// -----
+
+spirv.module Logical GLSL450 {
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // expected-error @+1 {{'spirv.ExecutionMode' op expected 0 value operand(s), got 1}}
+ spirv.ExecutionMode @do_nothing "ContractionOff", 3
+}
+
+// -----
+
+spirv.module Logical GLSL450 {
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // expected-error @+1 {{'spirv.ExecutionMode' op expected 1 value operand(s), got 2}}
+ spirv.ExecutionMode @do_nothing "SubgroupSize", 3, 4
+}
+
+// -----
+
+spirv.module Logical GLSL450 {
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // expected-error @+1 {{'spirv.ExecutionMode' op expected 3 value operand(s), got 2}}
+ spirv.ExecutionMode @do_nothing "LocalSize", 3, 4
+}
+
+// -----
+
//===----------------------------------------------------------------------===//
// spirv.ExecutionModeId
//===----------------------------------------------------------------------===//
@@ -423,6 +526,44 @@ spirv.module Logical GLSL450 {
// -----
+spirv.module Logical GLSL450 {
+ spirv.SpecConstant @x = 3 : i32
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // expected-error @+1 {{'spirv.ExecutionModeId' op expected ExecutionMode that takes extra operands that are <id> operands, got: LocalSize}}
+ spirv.ExecutionModeId @do_nothing "LocalSize" @x
+}
+
+// -----
+
+spirv.module Logical GLSL450 {
+ spirv.SpecConstant @x = 3 : i32
+ spirv.SpecConstant @y = 4 : i32
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // expected-error @+1 {{'spirv.ExecutionModeId' op expected 1 value operand(s), got 2}}
+ spirv.ExecutionModeId @do_nothing "SubgroupsPerWorkgroupId" @x, @y
+}
+
+// -----
+
+spirv.module Logical GLSL450 {
+ spirv.SpecConstant @x = 3 : i32
+ spirv.SpecConstant @y = 4 : i32
+ spirv.func @do_nothing() -> () "None" {
+ spirv.Return
+ }
+ spirv.EntryPoint "GLCompute" @do_nothing
+ // expected-error @+1 {{'spirv.ExecutionModeId' op expected 3 value operand(s), got 2}}
+ spirv.ExecutionModeId @do_nothing "LocalSizeId" @x, @y
+}
+
+// -----
+
//===----------------------------------------------------------------------===//
// spirv.func
//===----------------------------------------------------------------------===//
More information about the Mlir-commits
mailing list