[Mlir-commits] [mlir] [mlir][spirv] Update verifier for `spirv.mlir.merge` (PR #133427)

Longsheng Mou llvmlistbot at llvm.org
Fri Mar 28 18:17:27 PDT 2025


https://github.com/CoTinker updated https://github.com/llvm/llvm-project/pull/133427

>From c86bb3014698821f3a4d1cb5f432f8108487ad85 Mon Sep 17 00:00:00 2001
From: Longsheng Mou <longshengmou at gmail.com>
Date: Fri, 28 Mar 2025 15:52:48 +0800
Subject: [PATCH 1/2] [mlir][spirv] Update verifier for `spirv.mlir.merge`

- Moves the verification logic to the `verifyRegions` method of the parent operation.
- Fixes a crash during verification when the last block lacks a terminator.
---
 .../Dialect/SPIRV/IR/SPIRVControlFlowOps.td   |  3 ++-
 mlir/lib/Dialect/SPIRV/IR/ControlFlowOps.cpp  | 24 ++++++++++-------
 .../Dialect/SPIRV/IR/control-flow-ops.mlir    | 27 ++++++++++++-------
 3 files changed, 33 insertions(+), 21 deletions(-)

diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td
index ade20f915c0c3..ec05e803e55be 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td
@@ -351,7 +351,8 @@ def SPIRV_LoopOp : SPIRV_Op<"mlir.loop", [InFunctionScope]> {
 
 // -----
 
-def SPIRV_MergeOp : SPIRV_Op<"mlir.merge", [Pure, Terminator]> {
+def SPIRV_MergeOp : SPIRV_Op<"mlir.merge", [
+    Pure, Terminator, ParentOneOf<["SelectionOp", "LoopOp"]>]> {
   let summary = "A special terminator for merging a structured selection/loop.";
 
   let description = [{
diff --git a/mlir/lib/Dialect/SPIRV/IR/ControlFlowOps.cpp b/mlir/lib/Dialect/SPIRV/IR/ControlFlowOps.cpp
index 191bb330df756..407769502436c 100644
--- a/mlir/lib/Dialect/SPIRV/IR/ControlFlowOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/ControlFlowOps.cpp
@@ -259,6 +259,13 @@ static bool isMergeBlock(Block &block) {
          isa<spirv::MergeOp>(block.front());
 }
 
+/// Returns true if a `spirv.mlir.merge` op outside the merge block.
+static bool hasOtherMerge(Region &region) {
+  return !region.empty() && llvm::any_of(region.getOps(), [&](Operation &op) {
+    return isa<spirv::MergeOp>(op) && op.getBlock() != &region.back();
+  });
+}
+
 LogicalResult LoopOp::verifyRegions() {
   auto *op = getOperation();
 
@@ -298,6 +305,9 @@ LogicalResult LoopOp::verifyRegions() {
   if (!isMergeBlock(merge))
     return emitOpError("last block must be the merge block with only one "
                        "'spirv.mlir.merge' op");
+  if (hasOtherMerge(region))
+    return emitOpError(
+        "should not have 'spirv.mlir.merge' op outside the merge block");
 
   if (std::next(region.begin()) == region.end())
     return emitOpError(
@@ -382,16 +392,7 @@ void LoopOp::addEntryAndMergeBlock(OpBuilder &builder) {
 //===----------------------------------------------------------------------===//
 
 LogicalResult MergeOp::verify() {
-  auto *parentOp = (*this)->getParentOp();
-  if (!parentOp || !isa<spirv::SelectionOp, spirv::LoopOp>(parentOp))
-    return emitOpError(
-        "expected parent op to be 'spirv.mlir.selection' or 'spirv.mlir.loop'");
-
-  // TODO: This check should be done in `verifyRegions` of parent op.
-  Block &parentLastBlock = (*this)->getParentRegion()->back();
-  if (getOperation() != parentLastBlock.getTerminator())
-    return emitOpError("can only be used in the last block of "
-                       "'spirv.mlir.selection' or 'spirv.mlir.loop'");
+  // Verification is performed in parent op.
   return success();
 }
 
@@ -507,6 +508,9 @@ LogicalResult SelectionOp::verifyRegions() {
   if (!isMergeBlock(region.back()))
     return emitOpError("last block must be the merge block with only one "
                        "'spirv.mlir.merge' op");
+  if (hasOtherMerge(region))
+    return emitOpError(
+        "should not have 'spirv.mlir.merge' op outside the merge block");
 
   if (std::next(region.begin()) == region.end())
     return emitOpError("must have a selection header block");
diff --git a/mlir/test/Dialect/SPIRV/IR/control-flow-ops.mlir b/mlir/test/Dialect/SPIRV/IR/control-flow-ops.mlir
index 1d1e2840a579a..188a55d755fd2 100644
--- a/mlir/test/Dialect/SPIRV/IR/control-flow-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/control-flow-ops.mlir
@@ -431,29 +431,36 @@ func.func @only_entry_and_continue_branch_to_header() -> () {
 //===----------------------------------------------------------------------===//
 
 func.func @merge() -> () {
-  // expected-error @+1 {{expected parent op to be 'spirv.mlir.selection' or 'spirv.mlir.loop'}}
+  // expected-error @+1 {{expects parent op to be one of 'spirv.mlir.selection, spirv.mlir.loop'}}
   spirv.mlir.merge
 }
 
 // -----
 
 func.func @only_allowed_in_last_block(%cond : i1) -> () {
-  %zero = spirv.Constant 0: i32
-  %one = spirv.Constant 1: i32
-  %var = spirv.Variable init(%zero) : !spirv.ptr<i32, Function>
-
+  // expected-error @+1 {{'spirv.mlir.selection' op should not have 'spirv.mlir.merge' op outside the merge block}}
   spirv.mlir.selection {
     spirv.BranchConditional %cond, ^then, ^merge
-
   ^then:
-    spirv.Store "Function" %var, %one : i32
-    // expected-error @+1 {{can only be used in the last block of 'spirv.mlir.selection' or 'spirv.mlir.loop'}}
     spirv.mlir.merge
-
   ^merge:
     spirv.mlir.merge
   }
+  spirv.Return
+}
 
+// -----
+
+// Ensure this case not crash
+
+func.func @last_block_no_terminator(%cond : i1) -> () {
+  // expected-error @+1 {{empty block: expect at least a terminator}}
+  spirv.mlir.selection {
+    spirv.BranchConditional %cond, ^then, ^merge
+  ^then:
+    spirv.mlir.merge
+  ^merge:
+  }
   spirv.Return
 }
 
@@ -461,12 +468,12 @@ func.func @only_allowed_in_last_block(%cond : i1) -> () {
 
 func.func @only_allowed_in_last_block() -> () {
   %true = spirv.Constant true
+  // expected-error @+1 {{'spirv.mlir.loop' op should not have 'spirv.mlir.merge' op outside the merge block}}
   spirv.mlir.loop {
     spirv.Branch ^header
   ^header:
     spirv.BranchConditional %true, ^body, ^merge
   ^body:
-    // expected-error @+1 {{can only be used in the last block of 'spirv.mlir.selection' or 'spirv.mlir.loop'}}
     spirv.mlir.merge
   ^continue:
     spirv.Branch ^header

>From d7efba45a0d525854769f0f7adf927ee71069323 Mon Sep 17 00:00:00 2001
From: Longsheng Mou <longshengmou at gmail.com>
Date: Sat, 29 Mar 2025 09:17:19 +0800
Subject: [PATCH 2/2] disable verifier

---
 mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td
index ec05e803e55be..cb7d27e8d4b9a 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVControlFlowOps.td
@@ -372,6 +372,8 @@ def SPIRV_MergeOp : SPIRV_Op<"mlir.merge", [
   let hasOpcode = 0;
 
   let autogenSerialization = 0;
+
+  let hasVerifier = 0;
 }
 
 // -----



More information about the Mlir-commits mailing list