[Mlir-commits] [mlir] [mlir][spirv] Update verifier for `spirv.mlir.merge` (PR #133427)
Longsheng Mou
llvmlistbot at llvm.org
Fri Mar 28 04:26:11 PDT 2025
https://github.com/CoTinker created https://github.com/llvm/llvm-project/pull/133427
- Moves the verification logic to the `verifyRegions` method of the parent operation.
- Fixes a crash during verification when the last block lacks a terminator.
Fixes #132850.
>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] [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 ®ion) {
+ return !region.empty() && llvm::any_of(region.getOps(), [&](Operation &op) {
+ return isa<spirv::MergeOp>(op) && op.getBlock() != ®ion.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
More information about the Mlir-commits
mailing list