[Mlir-commits] [mlir] [mlir-c] Add structural operation equivalence (PR #206537)

Maksim Levental llvmlistbot at llvm.org
Sat Aug 1 01:26:54 PDT 2026


https://github.com/makslevental updated https://github.com/llvm/llvm-project/pull/206537

>From 50b15d698817f7089a9b3ecda71c20f3ee450de2 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Mon, 29 Jun 2026 10:14:49 -0700
Subject: [PATCH 1/2] [mlir-c] Add structural operation equivalence

---
 mlir/include/mlir-c/IR.h | 25 ++++++++++++++
 mlir/lib/CAPI/IR/IR.cpp  | 24 ++++++++++++++
 mlir/test/CAPI/ir.c      | 70 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+)

diff --git a/mlir/include/mlir-c/IR.h b/mlir/include/mlir-c/IR.h
index ba838043c4b87..6355945070a5b 100644
--- a/mlir/include/mlir-c/IR.h
+++ b/mlir/include/mlir-c/IR.h
@@ -642,6 +642,31 @@ MLIR_CAPI_EXPORTED bool mlirOperationEqual(MlirOperation op,
 /// Compute a hash for the given operation.
 MLIR_CAPI_EXPORTED size_t mlirOperationHashValue(MlirOperation op);
 
+/// Flags controlling structural operation equivalence and hashing. These mirror
+/// `mlir::OperationEquivalence::Flags` and may be combined with bitwise OR.
+typedef enum MlirOperationEquivalenceFlags {
+  /// No flags: locations, discardable attributes, properties and
+  /// commutativity are all significant.
+  MLIR_OPERATION_EQUIVALENCE_NONE = 0,
+  /// Ignore the locations attached to operations.
+  MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS = 1,
+  /// Ignore the discardable attributes attached to operations.
+  MLIR_OPERATION_EQUIVALENCE_IGNORE_DISCARDABLE_ATTRS = 2,
+  /// Ignore the properties attached to operations.
+  MLIR_OPERATION_EQUIVALENCE_IGNORE_PROPERTIES = 4,
+  /// Ignore commutativity, comparing operands in an order-sensitive way.
+  MLIR_OPERATION_EQUIVALENCE_IGNORE_COMMUTATIVITY = 8,
+} MlirOperationEquivalenceFlags;
+
+/// Checks whether two operations are structurally equivalent, i.e. they have
+/// the same name, attributes, operand and result types, and recursively
+/// equivalent regions. Operand equivalence is tracked structurally during the
+/// traversal (operands need not be the exact same SSA values). The comparison
+/// is parameterized by `flags` (see MlirOperationEquivalenceFlags).
+MLIR_CAPI_EXPORTED bool
+mlirOperationIsStructurallyEquivalent(MlirOperation lhs, MlirOperation rhs,
+                                      MlirOperationEquivalenceFlags flags);
+
 /// Gets the context this operation is associated with
 MLIR_CAPI_EXPORTED MlirContext mlirOperationGetContext(MlirOperation op);
 
diff --git a/mlir/lib/CAPI/IR/IR.cpp b/mlir/lib/CAPI/IR/IR.cpp
index bceed8eeea4a9..cb7f56aa31088 100644
--- a/mlir/lib/CAPI/IR/IR.cpp
+++ b/mlir/lib/CAPI/IR/IR.cpp
@@ -661,6 +661,30 @@ size_t mlirOperationHashValue(MlirOperation op) {
   return OperationEquivalence::computeHash(unwrap(op));
 }
 
+/// Translates the C equivalence flags to mlir::OperationEquivalence::Flags. The
+/// enumerator values are kept in sync, asserted below.
+static OperationEquivalence::Flags
+unwrapEquivalenceFlags(MlirOperationEquivalenceFlags flags) {
+  static_assert(MLIR_OPERATION_EQUIVALENCE_NONE == OperationEquivalence::None &&
+                    MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS ==
+                        OperationEquivalence::IgnoreLocations &&
+                    MLIR_OPERATION_EQUIVALENCE_IGNORE_DISCARDABLE_ATTRS ==
+                        OperationEquivalence::IgnoreDiscardableAttrs &&
+                    MLIR_OPERATION_EQUIVALENCE_IGNORE_PROPERTIES ==
+                        OperationEquivalence::IgnoreProperties &&
+                    MLIR_OPERATION_EQUIVALENCE_IGNORE_COMMUTATIVITY ==
+                        OperationEquivalence::IgnoreCommutativity,
+                "MlirOperationEquivalenceFlags out of sync with "
+                "OperationEquivalence::Flags");
+  return static_cast<OperationEquivalence::Flags>(flags);
+}
+
+bool mlirOperationIsStructurallyEquivalent(
+    MlirOperation lhs, MlirOperation rhs, MlirOperationEquivalenceFlags flags) {
+  return OperationEquivalence::isEquivalentTo(unwrap(lhs), unwrap(rhs),
+                                              unwrapEquivalenceFlags(flags));
+}
+
 MlirContext mlirOperationGetContext(MlirOperation op) {
   return wrap(unwrap(op)->getContext());
 }
diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c
index f73153bc54129..85fb494cb6498 100644
--- a/mlir/test/CAPI/ir.c
+++ b/mlir/test/CAPI/ir.c
@@ -3034,6 +3034,74 @@ int testReplaceUsesWithIf(MlirContext ctx) {
   return 0;
 }
 
+int testOperationEquivalence(MlirContext ctx) {
+  fprintf(stderr, "@testOperationEquivalence\n");
+  // CHECK-LABEL: @testOperationEquivalence
+
+  mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("arith"));
+
+  const char *moduleStr = "func.func @f(%arg0: i32) -> i32 {\n"
+                          "  %0 = arith.constant 42 : i32\n"
+                          "  %1 = arith.constant 42 : i32\n"
+                          "  %2 = arith.constant 7 : i32\n"
+                          "  %3 = arith.subi %0, %2 : i32\n"
+                          "  %4 = arith.subi %0, %2 : i32\n"
+                          "  %5 = arith.subi %2, %0 : i32\n"
+                          "  return %0 : i32\n"
+                          "}\n";
+  MlirModule module =
+      mlirModuleCreateParse(ctx, mlirStringRefCreateFromCString(moduleStr));
+
+  MlirBlock moduleBody = mlirModuleGetBody(module);
+  MlirOperation funcOp = mlirBlockGetFirstOperation(moduleBody);
+  MlirRegion funcRegion = mlirOperationGetRegion(funcOp, 0);
+  MlirBlock funcBody = mlirRegionGetFirstBlock(funcRegion);
+
+  MlirOperation c42a = mlirBlockGetFirstOperation(funcBody);
+  MlirOperation c42b = mlirOperationGetNextInBlock(c42a);
+  MlirOperation c7 = mlirOperationGetNextInBlock(c42b);
+  MlirOperation sub3 = mlirOperationGetNextInBlock(c7);
+  MlirOperation sub4 = mlirOperationGetNextInBlock(sub3);
+  MlirOperation sub5 = mlirOperationGetNextInBlock(sub4);
+
+  // Two identical constants are structurally equivalent when locations are
+  // ignored, even though their result SSA values and source locations differ.
+  assert(mlirOperationIsStructurallyEquivalent(
+      c42a, c42b, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  // Without ignoring locations they differ, since they sit on distinct lines.
+  assert(!mlirOperationIsStructurallyEquivalent(
+      c42a, c42b, MLIR_OPERATION_EQUIVALENCE_NONE));
+
+  // A constant with a different value is not equivalent.
+  assert(!mlirOperationIsStructurallyEquivalent(
+      c42a, c7, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  // Equivalence is reflexive.
+  assert(mlirOperationIsStructurallyEquivalent(
+      c42a, c42a, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  // `%3 = subi %0, %2` and `%4 = subi %0, %2` use the exact same operands, so
+  // they are equivalent.
+  assert(mlirOperationIsStructurallyEquivalent(
+      sub3, sub4, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  // `%5 = subi %2, %0` swaps the operands; since subi is not commutative this
+  // is not equivalent to `%3 = subi %0, %2`.
+  assert(!mlirOperationIsStructurallyEquivalent(
+      sub3, sub5, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  // A constant and a subtraction are different operations.
+  assert(!mlirOperationIsStructurallyEquivalent(
+      c42a, sub3, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  mlirModuleDestroy(module);
+
+  // CHECK: testOperationEquivalence: PASSED
+  fprintf(stderr, "testOperationEquivalence: PASSED\n");
+  return 0;
+}
+
 int main(void) {
   MlirContext ctx = mlirContextCreate();
   registerAllUpstreamDialects(ctx);
@@ -3091,6 +3159,8 @@ int main(void) {
     return 20;
   if (testReplaceUsesWithIf(ctx))
     return 21;
+  if (testOperationEquivalence(ctx))
+    return 22;
 
   // CHECK: DESTROY MAIN CONTEXT
   // CHECK: reportResourceDelete: resource_i64_blob

>From a2756bd3087ef6310cca3d4e5f24428815259262 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Thu, 30 Jul 2026 14:09:41 -0700
Subject: [PATCH 2/2] Address review: fix -Wenum-compare build failure, add
 structural hash, expand flag coverage

---
 mlir/include/mlir-c/IR.h |  26 ++++++---
 mlir/lib/CAPI/IR/IR.cpp  |  27 ++++-----
 mlir/test/CAPI/ir.c      | 117 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 145 insertions(+), 25 deletions(-)

diff --git a/mlir/include/mlir-c/IR.h b/mlir/include/mlir-c/IR.h
index 6355945070a5b..ac8ffbaf028b0 100644
--- a/mlir/include/mlir-c/IR.h
+++ b/mlir/include/mlir-c/IR.h
@@ -639,7 +639,10 @@ static inline bool mlirOperationIsNull(MlirOperation op) { return !op.ptr; }
 MLIR_CAPI_EXPORTED bool mlirOperationEqual(MlirOperation op,
                                            MlirOperation other);
 
-/// Compute a hash for the given operation.
+/// Compute a hash for the given operation. Operand and result SSA values are
+/// hashed by identity and locations are significant, so equivalent-but-distinct
+/// operations hash differently; use mlirOperationStructuralHashValue for a hash
+/// that pairs with mlirOperationIsStructurallyEquivalent.
 MLIR_CAPI_EXPORTED size_t mlirOperationHashValue(MlirOperation op);
 
 /// Flags controlling structural operation equivalence and hashing. These mirror
@@ -660,12 +663,21 @@ typedef enum MlirOperationEquivalenceFlags {
 
 /// Checks whether two operations are structurally equivalent, i.e. they have
 /// the same name, attributes, operand and result types, and recursively
-/// equivalent regions. Operand equivalence is tracked structurally during the
-/// traversal (operands need not be the exact same SSA values). The comparison
-/// is parameterized by `flags` (see MlirOperationEquivalenceFlags).
-MLIR_CAPI_EXPORTED bool
-mlirOperationIsStructurallyEquivalent(MlirOperation lhs, MlirOperation rhs,
-                                      MlirOperationEquivalenceFlags flags);
+/// equivalent regions. Operand equivalence is tracked structurally while
+/// recursing into regions, so operands defined inside the compared regions need
+/// not be the exact same SSA values; operands defined outside must be. `flags`
+/// is a bitwise OR of MlirOperationEquivalenceFlags values.
+MLIR_CAPI_EXPORTED bool mlirOperationIsStructurallyEquivalent(MlirOperation lhs,
+                                                              MlirOperation rhs,
+                                                              uint32_t flags);
+
+/// Computes a hash for the given operation that pairs with
+/// mlirOperationIsStructurallyEquivalent: two operations that are structurally
+/// equivalent under the same `flags` hash equally. Operands are hashed by
+/// identity, results are not hashed at all, and regions do not participate in
+/// the hash. `flags` is a bitwise OR of MlirOperationEquivalenceFlags values.
+MLIR_CAPI_EXPORTED size_t mlirOperationStructuralHashValue(MlirOperation op,
+                                                           uint32_t flags);
 
 /// Gets the context this operation is associated with
 MLIR_CAPI_EXPORTED MlirContext mlirOperationGetContext(MlirOperation op);
diff --git a/mlir/lib/CAPI/IR/IR.cpp b/mlir/lib/CAPI/IR/IR.cpp
index cb7f56aa31088..ef730b26cdd5d 100644
--- a/mlir/lib/CAPI/IR/IR.cpp
+++ b/mlir/lib/CAPI/IR/IR.cpp
@@ -662,29 +662,24 @@ size_t mlirOperationHashValue(MlirOperation op) {
 }
 
 /// Translates the C equivalence flags to mlir::OperationEquivalence::Flags. The
-/// enumerator values are kept in sync, asserted below.
-static OperationEquivalence::Flags
-unwrapEquivalenceFlags(MlirOperationEquivalenceFlags flags) {
-  static_assert(MLIR_OPERATION_EQUIVALENCE_NONE == OperationEquivalence::None &&
-                    MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS ==
-                        OperationEquivalence::IgnoreLocations &&
-                    MLIR_OPERATION_EQUIVALENCE_IGNORE_DISCARDABLE_ATTRS ==
-                        OperationEquivalence::IgnoreDiscardableAttrs &&
-                    MLIR_OPERATION_EQUIVALENCE_IGNORE_PROPERTIES ==
-                        OperationEquivalence::IgnoreProperties &&
-                    MLIR_OPERATION_EQUIVALENCE_IGNORE_COMMUTATIVITY ==
-                        OperationEquivalence::IgnoreCommutativity,
-                "MlirOperationEquivalenceFlags out of sync with "
-                "OperationEquivalence::Flags");
+/// enumerator values mirror each other.
+static OperationEquivalence::Flags unwrapEquivalenceFlags(uint32_t flags) {
   return static_cast<OperationEquivalence::Flags>(flags);
 }
 
-bool mlirOperationIsStructurallyEquivalent(
-    MlirOperation lhs, MlirOperation rhs, MlirOperationEquivalenceFlags flags) {
+bool mlirOperationIsStructurallyEquivalent(MlirOperation lhs, MlirOperation rhs,
+                                           uint32_t flags) {
   return OperationEquivalence::isEquivalentTo(unwrap(lhs), unwrap(rhs),
                                               unwrapEquivalenceFlags(flags));
 }
 
+size_t mlirOperationStructuralHashValue(MlirOperation op, uint32_t flags) {
+  return OperationEquivalence::computeHash(
+      unwrap(op), /*hashOperands=*/OperationEquivalence::directHashValue,
+      /*hashResults=*/OperationEquivalence::ignoreHashValue,
+      unwrapEquivalenceFlags(flags));
+}
+
 MlirContext mlirOperationGetContext(MlirOperation op) {
   return wrap(unwrap(op)->getContext());
 }
diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c
index 85fb494cb6498..bd9c8e0f50778 100644
--- a/mlir/test/CAPI/ir.c
+++ b/mlir/test/CAPI/ir.c
@@ -3047,6 +3047,10 @@ int testOperationEquivalence(MlirContext ctx) {
                           "  %3 = arith.subi %0, %2 : i32\n"
                           "  %4 = arith.subi %0, %2 : i32\n"
                           "  %5 = arith.subi %2, %0 : i32\n"
+                          "  %6 = arith.subi %0, %2 {dialect.discardable} : "
+                          "i32\n"
+                          "  %7 = arith.addi %0, %2 : i32\n"
+                          "  %8 = arith.addi %2, %0 : i32\n"
                           "  return %0 : i32\n"
                           "}\n";
   MlirModule module =
@@ -3063,6 +3067,9 @@ int testOperationEquivalence(MlirContext ctx) {
   MlirOperation sub3 = mlirOperationGetNextInBlock(c7);
   MlirOperation sub4 = mlirOperationGetNextInBlock(sub3);
   MlirOperation sub5 = mlirOperationGetNextInBlock(sub4);
+  MlirOperation sub6 = mlirOperationGetNextInBlock(sub5);
+  MlirOperation add7 = mlirOperationGetNextInBlock(sub6);
+  MlirOperation add8 = mlirOperationGetNextInBlock(add7);
 
   // Two identical constants are structurally equivalent when locations are
   // ignored, even though their result SSA values and source locations differ.
@@ -3086,8 +3093,10 @@ int testOperationEquivalence(MlirContext ctx) {
   assert(mlirOperationIsStructurallyEquivalent(
       sub3, sub4, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
 
-  // `%5 = subi %2, %0` swaps the operands; since subi is not commutative this
-  // is not equivalent to `%3 = subi %0, %2`.
+  // `%5 = subi %2, %0` is not equivalent to `%3 = subi %0, %2`: `subi` is not
+  // commutative, so operands are compared pairwise, and the very first pair
+  // (`%0` = 42 vs `%2` = 7) already mismatches. See the `addi` pair below for
+  // the commutative path.
   assert(!mlirOperationIsStructurallyEquivalent(
       sub3, sub5, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
 
@@ -3095,8 +3104,112 @@ int testOperationEquivalence(MlirContext ctx) {
   assert(!mlirOperationIsStructurallyEquivalent(
       c42a, sub3, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
 
+  // `%6 = subi %0, %2 {dialect.discardable}` carries a discardable attribute
+  // that `%3 = subi %0, %2` does not, so the two differ unless discardable
+  // attributes are ignored as well. This also exercises OR-ing flags together.
+  assert(!mlirOperationIsStructurallyEquivalent(
+      sub3, sub6, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+  assert(mlirOperationIsStructurallyEquivalent(
+      sub3, sub6,
+      MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS |
+          MLIR_OPERATION_EQUIVALENCE_IGNORE_DISCARDABLE_ATTRS));
+
+  // `%7 = addi %0, %2` and `%8 = addi %2, %0` have swapped operands, but `addi`
+  // is commutative, so they are equivalent through the commutative path and
+  // only differ once commutativity is explicitly ignored.
+  assert(mlirOperationIsStructurallyEquivalent(
+      add7, add8, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+  assert(!mlirOperationIsStructurallyEquivalent(
+      add7, add8,
+      MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS |
+          MLIR_OPERATION_EQUIVALENCE_IGNORE_COMMUTATIVITY));
+
+  // The `value` of arith.constant is an inherent attribute held in the
+  // operation's properties, so ignoring properties makes constants with
+  // different values equivalent.
+  assert(mlirOperationIsStructurallyEquivalent(
+      c42a, c7,
+      MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS |
+          MLIR_OPERATION_EQUIVALENCE_IGNORE_PROPERTIES));
+
+  // The structural hash pairs with the equivalence above: operations that are
+  // equivalent under a set of flags hash equally under the same flags.
+  assert(mlirOperationStructuralHashValue(
+             c42a, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS) ==
+         mlirOperationStructuralHashValue(
+             c42b, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+  assert(mlirOperationStructuralHashValue(
+             sub3, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS) ==
+         mlirOperationStructuralHashValue(
+             sub4, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+  assert(mlirOperationStructuralHashValue(
+             sub3, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS |
+                       MLIR_OPERATION_EQUIVALENCE_IGNORE_DISCARDABLE_ATTRS) ==
+         mlirOperationStructuralHashValue(
+             sub6, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS |
+                       MLIR_OPERATION_EQUIVALENCE_IGNORE_DISCARDABLE_ATTRS));
+  // Commutative operands are folded into the hash in an order-insensitive way,
+  // matching the commutative equivalence above.
+  assert(mlirOperationStructuralHashValue(
+             add7, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS) ==
+         mlirOperationStructuralHashValue(
+             add8, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  // The flags are threaded through to the hash: locations and discardable
+  // attributes are hashed unless the corresponding flag is set. Hash
+  // inequality is not part of the contract, but these inputs do differ.
+  assert(
+      mlirOperationStructuralHashValue(c42a, MLIR_OPERATION_EQUIVALENCE_NONE) !=
+      mlirOperationStructuralHashValue(c42b, MLIR_OPERATION_EQUIVALENCE_NONE));
+  assert(mlirOperationStructuralHashValue(
+             sub3, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS) !=
+         mlirOperationStructuralHashValue(
+             sub6, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
   mlirModuleDestroy(module);
 
+  // Operands defined *inside* the compared regions need not be the same SSA
+  // values: recursing into the regions marks the two inner constants
+  // equivalent, and the `addi` operands are then matched through that mapping.
+  const char *regionModuleStr = "func.func @g() {\n"
+                                "  %r0 = scf.execute_region -> i32 {\n"
+                                "    %c = arith.constant 1 : i32\n"
+                                "    %d = arith.addi %c, %c : i32\n"
+                                "    scf.yield %d : i32\n"
+                                "  }\n"
+                                "  %r1 = scf.execute_region -> i32 {\n"
+                                "    %c = arith.constant 1 : i32\n"
+                                "    %d = arith.addi %c, %c : i32\n"
+                                "    scf.yield %d : i32\n"
+                                "  }\n"
+                                "  return\n"
+                                "}\n";
+  MlirModule regionModule = mlirModuleCreateParse(
+      ctx, mlirStringRefCreateFromCString(regionModuleStr));
+  assert(!mlirModuleIsNull(regionModule));
+  MlirBlock regionFuncBody = mlirRegionGetFirstBlock(mlirOperationGetRegion(
+      mlirBlockGetFirstOperation(mlirModuleGetBody(regionModule)), 0));
+  MlirOperation exec0 = mlirBlockGetFirstOperation(regionFuncBody);
+  MlirOperation exec1 = mlirOperationGetNextInBlock(exec0);
+  assert(mlirOperationIsStructurallyEquivalent(
+      exec0, exec1, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  // The nested `addi` operations compared on their own are *not* equivalent:
+  // the value mapping starts out empty, so operands defined outside the
+  // compared operation must be the exact same SSA values.
+  MlirBlock exec0Body =
+      mlirRegionGetFirstBlock(mlirOperationGetRegion(exec0, 0));
+  MlirBlock exec1Body =
+      mlirRegionGetFirstBlock(mlirOperationGetRegion(exec1, 0));
+  MlirOperation innerAdd0 =
+      mlirOperationGetNextInBlock(mlirBlockGetFirstOperation(exec0Body));
+  MlirOperation innerAdd1 =
+      mlirOperationGetNextInBlock(mlirBlockGetFirstOperation(exec1Body));
+  assert(!mlirOperationIsStructurallyEquivalent(
+      innerAdd0, innerAdd1, MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS));
+
+  mlirModuleDestroy(regionModule);
+
   // CHECK: testOperationEquivalence: PASSED
   fprintf(stderr, "testOperationEquivalence: PASSED\n");
   return 0;



More information about the Mlir-commits mailing list