[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