[Mlir-commits] [mlir] [mlir-c] Add mlirOperationIsAncestor and mlirOperationIsProperAncestor (PR #206556)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Sep 23 22:13:03 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Maksim Levental (makslevental)

<details>
<summary>Changes</summary>

Exposes `Operation::isAncestor` and `Operation::isProperAncestor` through the MLIR C API.

Assisted by: Claude

---
Full diff: https://github.com/llvm/llvm-project/pull/206556.diff


3 Files Affected:

- (modified) mlir/include/mlir-c/IR.h (+10) 
- (modified) mlir/lib/CAPI/IR/IR.cpp (+8) 
- (modified) mlir/test/CAPI/ir.c (+64) 


``````````diff
diff --git a/mlir/include/mlir-c/IR.h b/mlir/include/mlir-c/IR.h
index 7e9be93474296..67d0440042d7c 100644
--- a/mlir/include/mlir-c/IR.h
+++ b/mlir/include/mlir-c/IR.h
@@ -725,6 +725,16 @@ MLIR_CAPI_EXPORTED MlirBlock mlirOperationGetBlock(MlirOperation op);
 MLIR_CAPI_EXPORTED MlirOperation
 mlirOperationGetParentOperation(MlirOperation op);
 
+/// Returns true if `a` is an ancestor of `b`, i.e. `a` contains `b` or
+/// `a == b`.
+MLIR_CAPI_EXPORTED bool mlirOperationIsAncestor(MlirOperation a,
+                                                MlirOperation b);
+
+/// Returns true if `a` is a proper ancestor of `b`, i.e. `a` contains `b` but
+/// `a != b`.
+MLIR_CAPI_EXPORTED bool mlirOperationIsProperAncestor(MlirOperation a,
+                                                      MlirOperation b);
+
 /// Returns the number of regions attached to the given operation.
 MLIR_CAPI_EXPORTED intptr_t mlirOperationGetNumRegions(MlirOperation op);
 
diff --git a/mlir/lib/CAPI/IR/IR.cpp b/mlir/lib/CAPI/IR/IR.cpp
index fdd4c9e52f9ba..e01e49559eb17 100644
--- a/mlir/lib/CAPI/IR/IR.cpp
+++ b/mlir/lib/CAPI/IR/IR.cpp
@@ -728,6 +728,14 @@ MlirOperation mlirOperationGetParentOperation(MlirOperation op) {
   return wrap(unwrap(op)->getParentOp());
 }
 
+bool mlirOperationIsAncestor(MlirOperation a, MlirOperation b) {
+  return unwrap(a)->isAncestor(unwrap(b));
+}
+
+bool mlirOperationIsProperAncestor(MlirOperation a, MlirOperation b) {
+  return unwrap(a)->isProperAncestor(unwrap(b));
+}
+
 intptr_t mlirOperationGetNumRegions(MlirOperation op) {
   return static_cast<intptr_t>(unwrap(op)->getNumRegions());
 }
diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c
index c66f00b2b8be1..477d229c555b1 100644
--- a/mlir/test/CAPI/ir.c
+++ b/mlir/test/CAPI/ir.c
@@ -3381,6 +3381,68 @@ int testOperationEquivalence(MlirContext ctx) {
   return 0;
 }
 
+int testOperationIsAncestor(MlirContext ctx) {
+  fprintf(stderr, "@testOperationIsAncestor\n");
+  // CHECK-LABEL: @testOperationIsAncestor
+
+  mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("arith"));
+
+  const char *moduleStr = "func.func @f() {\n"
+                          "  %c0 = arith.constant 0 : i32\n"
+                          "  return\n"
+                          "}\n"
+                          "func.func @g() {\n"
+                          "  %c1 = arith.constant 1 : i32\n"
+                          "  return\n"
+                          "}\n";
+  MlirModule module =
+      mlirModuleCreateParse(ctx, mlirStringRefCreateFromCString(moduleStr));
+  MlirOperation moduleOp = mlirModuleGetOperation(module);
+  MlirBlock moduleBody = mlirModuleGetBody(module);
+  MlirOperation funcOp = mlirBlockGetFirstOperation(moduleBody);
+  MlirRegion funcRegion = mlirOperationGetRegion(funcOp, 0);
+  MlirBlock funcBody = mlirRegionGetFirstBlock(funcRegion);
+  MlirOperation constOp = mlirBlockGetFirstOperation(funcBody);
+
+  // A sibling func @g, and the constant in its own body, are both unrelated to
+  // @f and @f's constant.
+  MlirOperation otherFuncOp = mlirOperationGetNextInBlock(funcOp);
+  MlirRegion otherFuncRegion = mlirOperationGetRegion(otherFuncOp, 0);
+  MlirBlock otherFuncBody = mlirRegionGetFirstBlock(otherFuncRegion);
+  MlirOperation otherConstOp = mlirBlockGetFirstOperation(otherFuncBody);
+
+  // The module and the func both (properly) contain the constant.
+  assert(mlirOperationIsAncestor(moduleOp, constOp));
+  assert(mlirOperationIsProperAncestor(moduleOp, constOp));
+  assert(mlirOperationIsAncestor(funcOp, constOp));
+  assert(mlirOperationIsProperAncestor(funcOp, constOp));
+
+  // An operation is its own ancestor, but not its own proper ancestor.
+  assert(mlirOperationIsAncestor(constOp, constOp));
+  assert(!mlirOperationIsProperAncestor(constOp, constOp));
+
+  // The containment relation is not symmetric.
+  assert(!mlirOperationIsAncestor(constOp, moduleOp));
+
+  // Operations in disjoint subtrees are genuinely unrelated: neither is an
+  // ancestor of the other, in either direction. The sibling funcs, and the
+  // constants living in their separate bodies, share the module as a common
+  // root but do not contain one another -- so an implementation that merely
+  // tested for a shared root would be caught here.
+  assert(!mlirOperationIsAncestor(funcOp, otherFuncOp));
+  assert(!mlirOperationIsAncestor(otherFuncOp, funcOp));
+  assert(!mlirOperationIsProperAncestor(funcOp, otherFuncOp));
+  assert(!mlirOperationIsAncestor(funcOp, otherConstOp));
+  assert(!mlirOperationIsAncestor(otherConstOp, constOp));
+  assert(!mlirOperationIsAncestor(constOp, otherConstOp));
+
+  mlirModuleDestroy(module);
+
+  // CHECK: testOperationIsAncestor: PASSED
+  fprintf(stderr, "testOperationIsAncestor: PASSED\n");
+  return 0;
+}
+
 int main(void) {
   MlirContext ctx = mlirContextCreate();
   registerAllUpstreamDialects(ctx);
@@ -3441,6 +3503,8 @@ int main(void) {
     return 21;
   if (testOperationEquivalence(ctx))
     return 22;
+  if (testOperationIsAncestor(ctx))
+    return 23;
 
   // CHECK: DESTROY MAIN CONTEXT
   // CHECK: reportResourceDelete: resource_i64_blob

``````````

</details>


https://github.com/llvm/llvm-project/pull/206556


More information about the Mlir-commits mailing list