[Mlir-commits] [mlir] [mlir-c] Add DominanceInfo and PostDominanceInfo C API bindings (PR #206156)

Maksim Levental llvmlistbot at llvm.org
Sun Jun 28 18:18:29 PDT 2026


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

>From 92664079df0023a365541b30c5f01f86caca5017 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Fri, 26 Jun 2026 12:06:56 -0700
Subject: [PATCH] [mlir-c] Add DominanceInfo and PostDominanceInfo C API
 bindings

Expose DominanceInfo and PostDominanceInfo through the MLIR C API with
operation/block/value dominance queries, nearest common dominator,
reachability, and invalidation.
---
 mlir/include/mlir-c/Dominance.h    | 125 ++++++++++++++++++++++
 mlir/include/mlir/CAPI/Dominance.h |  19 ++++
 mlir/lib/CAPI/IR/CMakeLists.txt    |   1 +
 mlir/lib/CAPI/IR/Dominance.cpp     | 106 +++++++++++++++++++
 mlir/test/CAPI/ir.c                | 160 +++++++++++++++++++++++++++++
 5 files changed, 411 insertions(+)
 create mode 100644 mlir/include/mlir-c/Dominance.h
 create mode 100644 mlir/include/mlir/CAPI/Dominance.h
 create mode 100644 mlir/lib/CAPI/IR/Dominance.cpp

diff --git a/mlir/include/mlir-c/Dominance.h b/mlir/include/mlir-c/Dominance.h
new file mode 100644
index 0000000000000..2c18a8c9e7dd9
--- /dev/null
+++ b/mlir/include/mlir-c/Dominance.h
@@ -0,0 +1,125 @@
+//===- Dominance.h - C API for Dominance Analysis -----------------*- C -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_C_DOMINANCE_H
+#define MLIR_C_DOMINANCE_H
+
+#include "mlir-c/IR.h"
+#include "mlir-c/Support.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFINE_C_API_STRUCT(name, storage)                                     \
+  struct name {                                                                \
+    storage *ptr;                                                              \
+  };                                                                           \
+  typedef struct name name
+
+DEFINE_C_API_STRUCT(MlirDominanceInfo, void);
+DEFINE_C_API_STRUCT(MlirPostDominanceInfo, void);
+
+#undef DEFINE_C_API_STRUCT
+
+//===----------------------------------------------------------------------===//
+// DominanceInfo API
+//===----------------------------------------------------------------------===//
+
+/// Creates a DominanceInfo for the given operation (typically a FuncOp or
+/// ModuleOp). The caller owns the returned object and must destroy it.
+MLIR_CAPI_EXPORTED MlirDominanceInfo mlirDominanceInfoCreate(MlirOperation op);
+
+/// Destroys the given DominanceInfo.
+MLIR_CAPI_EXPORTED void mlirDominanceInfoDestroy(MlirDominanceInfo info);
+
+/// Returns true if operation A properly dominates operation B.
+MLIR_CAPI_EXPORTED bool
+mlirDominanceInfoProperlyDominatesOperation(MlirDominanceInfo info,
+                                            MlirOperation a, MlirOperation b);
+
+/// Returns true if operation A dominates operation B (A == B or A properly
+/// dominates B).
+MLIR_CAPI_EXPORTED bool
+mlirDominanceInfoDominatesOperation(MlirDominanceInfo info, MlirOperation a,
+                                    MlirOperation b);
+
+/// Returns true if value A properly dominates operation B.
+MLIR_CAPI_EXPORTED bool
+mlirDominanceInfoValueProperlyDominates(MlirDominanceInfo info, MlirValue a,
+                                        MlirOperation b);
+
+/// Returns true if value A dominates operation B (the operation defining A is B
+/// or A properly dominates B).
+MLIR_CAPI_EXPORTED bool
+mlirDominanceInfoValueDominates(MlirDominanceInfo info, MlirValue a,
+                                MlirOperation b);
+
+/// Returns true if block A properly dominates block B.
+MLIR_CAPI_EXPORTED bool
+mlirDominanceInfoProperlyDominatesBlock(MlirDominanceInfo info, MlirBlock a,
+                                        MlirBlock b);
+
+/// Returns true if block A dominates block B.
+MLIR_CAPI_EXPORTED bool mlirDominanceInfoDominatesBlock(MlirDominanceInfo info,
+                                                        MlirBlock a,
+                                                        MlirBlock b);
+
+/// Finds the nearest common dominator of blocks A and B. Returns a null block
+/// if none exists.
+MLIR_CAPI_EXPORTED MlirBlock mlirDominanceInfoFindNearestCommonDominator(
+    MlirDominanceInfo info, MlirBlock a, MlirBlock b);
+
+/// Returns true if the given block is reachable from the entry block of its
+/// region.
+MLIR_CAPI_EXPORTED bool
+mlirDominanceInfoIsReachableFromEntry(MlirDominanceInfo info, MlirBlock block);
+
+/// Invalidates all cached dominance information.
+MLIR_CAPI_EXPORTED void mlirDominanceInfoInvalidate(MlirDominanceInfo info);
+
+//===----------------------------------------------------------------------===//
+// PostDominanceInfo API
+//===----------------------------------------------------------------------===//
+
+/// Creates a PostDominanceInfo for the given operation.
+MLIR_CAPI_EXPORTED MlirPostDominanceInfo
+mlirPostDominanceInfoCreate(MlirOperation op);
+
+/// Destroys the given PostDominanceInfo.
+MLIR_CAPI_EXPORTED void
+mlirPostDominanceInfoDestroy(MlirPostDominanceInfo info);
+
+/// Returns true if operation A properly post-dominates operation B.
+MLIR_CAPI_EXPORTED bool mlirPostDominanceInfoProperlyPostDominatesOperation(
+    MlirPostDominanceInfo info, MlirOperation a, MlirOperation b);
+
+/// Returns true if operation A post-dominates operation B.
+MLIR_CAPI_EXPORTED bool
+mlirPostDominanceInfoPostDominatesOperation(MlirPostDominanceInfo info,
+                                            MlirOperation a, MlirOperation b);
+
+/// Returns true if block A properly post-dominates block B.
+MLIR_CAPI_EXPORTED bool
+mlirPostDominanceInfoProperlyPostDominatesBlock(MlirPostDominanceInfo info,
+                                                MlirBlock a, MlirBlock b);
+
+/// Returns true if block A post-dominates block B.
+MLIR_CAPI_EXPORTED bool
+mlirPostDominanceInfoPostDominatesBlock(MlirPostDominanceInfo info, MlirBlock a,
+                                        MlirBlock b);
+
+/// Invalidates all cached post-dominance information.
+MLIR_CAPI_EXPORTED void
+mlirPostDominanceInfoInvalidate(MlirPostDominanceInfo info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MLIR_C_DOMINANCE_H
diff --git a/mlir/include/mlir/CAPI/Dominance.h b/mlir/include/mlir/CAPI/Dominance.h
new file mode 100644
index 0000000000000..b710087f32426
--- /dev/null
+++ b/mlir/include/mlir/CAPI/Dominance.h
@@ -0,0 +1,19 @@
+//===- Dominance.h - C API wrap/unwrap for Dominance -------------*- C++ -*===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_CAPI_DOMINANCE_H
+#define MLIR_CAPI_DOMINANCE_H
+
+#include "mlir-c/Dominance.h"
+#include "mlir/CAPI/Wrap.h"
+#include "mlir/IR/Dominance.h"
+
+DEFINE_C_API_PTR_METHODS(MlirDominanceInfo, mlir::DominanceInfo)
+DEFINE_C_API_PTR_METHODS(MlirPostDominanceInfo, mlir::PostDominanceInfo)
+
+#endif // MLIR_CAPI_DOMINANCE_H
diff --git a/mlir/lib/CAPI/IR/CMakeLists.txt b/mlir/lib/CAPI/IR/CMakeLists.txt
index d78f9d9735aa3..8bc76677d2bfd 100644
--- a/mlir/lib/CAPI/IR/CMakeLists.txt
+++ b/mlir/lib/CAPI/IR/CMakeLists.txt
@@ -6,6 +6,7 @@ add_mlir_upstream_c_api_library(MLIRCAPIIR
   BuiltinTypes.cpp
   Diagnostics.cpp
   DialectHandle.cpp
+  Dominance.cpp
   ExtensibleDialect.cpp
   IntegerSet.cpp
   IR.cpp
diff --git a/mlir/lib/CAPI/IR/Dominance.cpp b/mlir/lib/CAPI/IR/Dominance.cpp
new file mode 100644
index 0000000000000..83c4f5237aa16
--- /dev/null
+++ b/mlir/lib/CAPI/IR/Dominance.cpp
@@ -0,0 +1,106 @@
+//===- Dominance.cpp - C API for Dominance Analysis -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir-c/Dominance.h"
+#include "mlir/CAPI/Dominance.h"
+#include "mlir/CAPI/IR.h"
+
+using namespace mlir;
+
+//===----------------------------------------------------------------------===//
+// DominanceInfo API
+//===----------------------------------------------------------------------===//
+
+MlirDominanceInfo mlirDominanceInfoCreate(MlirOperation op) {
+  return wrap(new DominanceInfo(unwrap(op)));
+}
+
+void mlirDominanceInfoDestroy(MlirDominanceInfo info) { delete unwrap(info); }
+
+bool mlirDominanceInfoProperlyDominatesOperation(MlirDominanceInfo info,
+                                                 MlirOperation a,
+                                                 MlirOperation b) {
+  return unwrap(info)->properlyDominates(unwrap(a), unwrap(b));
+}
+
+bool mlirDominanceInfoDominatesOperation(MlirDominanceInfo info,
+                                         MlirOperation a, MlirOperation b) {
+  return unwrap(info)->dominates(unwrap(a), unwrap(b));
+}
+
+bool mlirDominanceInfoValueProperlyDominates(MlirDominanceInfo info,
+                                             MlirValue a, MlirOperation b) {
+  return unwrap(info)->properlyDominates(unwrap(a), unwrap(b));
+}
+
+bool mlirDominanceInfoValueDominates(MlirDominanceInfo info, MlirValue a,
+                                     MlirOperation b) {
+  return unwrap(info)->dominates(unwrap(a), unwrap(b));
+}
+
+bool mlirDominanceInfoProperlyDominatesBlock(MlirDominanceInfo info,
+                                             MlirBlock a, MlirBlock b) {
+  return unwrap(info)->properlyDominates(unwrap(a), unwrap(b));
+}
+
+bool mlirDominanceInfoDominatesBlock(MlirDominanceInfo info, MlirBlock a,
+                                     MlirBlock b) {
+  return unwrap(info)->dominates(unwrap(a), unwrap(b));
+}
+
+MlirBlock mlirDominanceInfoFindNearestCommonDominator(MlirDominanceInfo info,
+                                                      MlirBlock a,
+                                                      MlirBlock b) {
+  return wrap(unwrap(info)->findNearestCommonDominator(unwrap(a), unwrap(b)));
+}
+
+bool mlirDominanceInfoIsReachableFromEntry(MlirDominanceInfo info,
+                                           MlirBlock block) {
+  return unwrap(info)->isReachableFromEntry(unwrap(block));
+}
+
+void mlirDominanceInfoInvalidate(MlirDominanceInfo info) {
+  unwrap(info)->invalidate();
+}
+
+//===----------------------------------------------------------------------===//
+// PostDominanceInfo API
+//===----------------------------------------------------------------------===//
+
+MlirPostDominanceInfo mlirPostDominanceInfoCreate(MlirOperation op) {
+  return wrap(new PostDominanceInfo(unwrap(op)));
+}
+
+void mlirPostDominanceInfoDestroy(MlirPostDominanceInfo info) {
+  delete unwrap(info);
+}
+
+bool mlirPostDominanceInfoProperlyPostDominatesOperation(
+    MlirPostDominanceInfo info, MlirOperation a, MlirOperation b) {
+  return unwrap(info)->properlyPostDominates(unwrap(a), unwrap(b));
+}
+
+bool mlirPostDominanceInfoPostDominatesOperation(MlirPostDominanceInfo info,
+                                                 MlirOperation a,
+                                                 MlirOperation b) {
+  return unwrap(info)->postDominates(unwrap(a), unwrap(b));
+}
+
+bool mlirPostDominanceInfoProperlyPostDominatesBlock(MlirPostDominanceInfo info,
+                                                     MlirBlock a, MlirBlock b) {
+  return unwrap(info)->properlyPostDominates(unwrap(a), unwrap(b));
+}
+
+bool mlirPostDominanceInfoPostDominatesBlock(MlirPostDominanceInfo info,
+                                             MlirBlock a, MlirBlock b) {
+  return unwrap(info)->postDominates(unwrap(a), unwrap(b));
+}
+
+void mlirPostDominanceInfoInvalidate(MlirPostDominanceInfo info) {
+  unwrap(info)->invalidate();
+}
diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c
index d8a252a426f19..57ae8b9a2819b 100644
--- a/mlir/test/CAPI/ir.c
+++ b/mlir/test/CAPI/ir.c
@@ -17,6 +17,7 @@
 #include "mlir-c/BuiltinTypes.h"
 #include "mlir-c/Diagnostics.h"
 #include "mlir-c/Dialect/Func.h"
+#include "mlir-c/Dominance.h"
 #include "mlir-c/IntegerSet.h"
 #include "mlir-c/Interfaces.h"
 #include "mlir-c/RegisterEverything.h"
@@ -2742,6 +2743,163 @@ int testIRMapping(MlirContext ctx) {
   return 0;
 }
 
+int testDominanceInfo(MlirContext ctx) {
+  fprintf(stderr, "@testDominanceInfo\n");
+  // CHECK-LABEL: @testDominanceInfo
+
+  mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("arith"));
+  mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("cf"));
+
+  // Test operation dominance in a single block
+  const char *linearStr = "func.func @f(%arg0: i32) -> i32 {\n"
+                          "  %c0 = arith.constant 0 : i32\n"
+                          "  %c1 = arith.constant 1 : i32\n"
+                          "  %sum = arith.addi %c0, %c1 : i32\n"
+                          "  return %sum : i32\n"
+                          "}\n";
+  MlirModule linearModule =
+      mlirModuleCreateParse(ctx, mlirStringRefCreateFromCString(linearStr));
+  MlirBlock linearModuleBody = mlirModuleGetBody(linearModule);
+  MlirOperation linearFuncOp = mlirBlockGetFirstOperation(linearModuleBody);
+  MlirRegion linearFuncRegion = mlirOperationGetRegion(linearFuncOp, 0);
+  MlirBlock linearFuncBody = mlirRegionGetFirstBlock(linearFuncRegion);
+
+  MlirOperation c0Op = mlirBlockGetFirstOperation(linearFuncBody);
+  MlirOperation c1Op = mlirOperationGetNextInBlock(c0Op);
+  MlirOperation addOp = mlirOperationGetNextInBlock(c1Op);
+
+  MlirDominanceInfo domInfo = mlirDominanceInfoCreate(linearFuncOp);
+
+  // Earlier ops dominate later ops
+  assert(mlirDominanceInfoProperlyDominatesOperation(domInfo, c0Op, c1Op));
+  assert(mlirDominanceInfoProperlyDominatesOperation(domInfo, c0Op, addOp));
+  assert(mlirDominanceInfoProperlyDominatesOperation(domInfo, c1Op, addOp));
+  assert(!mlirDominanceInfoProperlyDominatesOperation(domInfo, addOp, c0Op));
+
+  // dominates includes self
+  assert(mlirDominanceInfoDominatesOperation(domInfo, c0Op, c0Op));
+  assert(!mlirDominanceInfoProperlyDominatesOperation(domInfo, c0Op, c0Op));
+
+  // Value dominance
+  MlirValue c0Result = mlirOperationGetResult(c0Op, 0);
+  assert(mlirDominanceInfoValueProperlyDominates(domInfo, c0Result, addOp));
+
+  // A value does not properly dominate an op that precedes its definition
+  MlirValue addResult = mlirOperationGetResult(addOp, 0);
+  assert(!mlirDominanceInfoValueProperlyDominates(domInfo, addResult, c0Op));
+
+  // dominates is reflexive on the defining op: a value dominates (but does not
+  // properly dominate) the op that defines it.
+  assert(mlirDominanceInfoValueDominates(domInfo, c0Result, c0Op));
+  assert(!mlirDominanceInfoValueProperlyDominates(domInfo, c0Result, c0Op));
+  assert(mlirDominanceInfoValueDominates(domInfo, c0Result, addOp));
+  assert(!mlirDominanceInfoValueDominates(domInfo, addResult, c0Op));
+
+  mlirDominanceInfoDestroy(domInfo);
+  mlirModuleDestroy(linearModule);
+
+  // Test block dominance with CFG
+  const char *cfgStr = "func.func @g(%cond: i1) -> i32 {\n"
+                       "  %c0 = arith.constant 0 : i32\n"
+                       "  %c1 = arith.constant 1 : i32\n"
+                       "  cf.cond_br %cond, ^bb1, ^bb2\n"
+                       "^bb1:\n"
+                       "  cf.br ^bb3(%c0 : i32)\n"
+                       "^bb2:\n"
+                       "  cf.br ^bb3(%c1 : i32)\n"
+                       "^bb3(%result: i32):\n"
+                       "  return %result : i32\n"
+                       "}\n";
+  MlirModule cfgModule =
+      mlirModuleCreateParse(ctx, mlirStringRefCreateFromCString(cfgStr));
+  MlirBlock cfgModuleBody = mlirModuleGetBody(cfgModule);
+  MlirOperation cfgFuncOp = mlirBlockGetFirstOperation(cfgModuleBody);
+  MlirRegion cfgFuncRegion = mlirOperationGetRegion(cfgFuncOp, 0);
+
+  MlirBlock bb0 = mlirRegionGetFirstBlock(cfgFuncRegion);
+  MlirBlock bb1 = mlirBlockGetNextInRegion(bb0);
+  MlirBlock bb2 = mlirBlockGetNextInRegion(bb1);
+  MlirBlock bb3 = mlirBlockGetNextInRegion(bb2);
+
+  MlirDominanceInfo cfgDomInfo = mlirDominanceInfoCreate(cfgFuncOp);
+
+  // Entry dominates all
+  assert(mlirDominanceInfoDominatesBlock(cfgDomInfo, bb0, bb1));
+  assert(mlirDominanceInfoDominatesBlock(cfgDomInfo, bb0, bb2));
+  assert(mlirDominanceInfoDominatesBlock(cfgDomInfo, bb0, bb3));
+
+  // Siblings don't dominate each other
+  assert(!mlirDominanceInfoDominatesBlock(cfgDomInfo, bb1, bb2));
+  assert(!mlirDominanceInfoDominatesBlock(cfgDomInfo, bb2, bb1));
+
+  // Self-dominance vs proper dominance
+  assert(mlirDominanceInfoDominatesBlock(cfgDomInfo, bb0, bb0));
+  assert(!mlirDominanceInfoProperlyDominatesBlock(cfgDomInfo, bb0, bb0));
+
+  // Proper dominance across distinct blocks: entry properly dominates the
+  // merge block, but neither branch does (bb3 is reachable from both).
+  assert(mlirDominanceInfoProperlyDominatesBlock(cfgDomInfo, bb0, bb3));
+  assert(!mlirDominanceInfoProperlyDominatesBlock(cfgDomInfo, bb1, bb3));
+
+  // Nearest common dominator
+  MlirBlock common =
+      mlirDominanceInfoFindNearestCommonDominator(cfgDomInfo, bb1, bb2);
+  assert(mlirBlockEqual(common, bb0));
+
+  // NCD of a block with itself is the block itself
+  assert(mlirBlockEqual(
+      mlirDominanceInfoFindNearestCommonDominator(cfgDomInfo, bb3, bb3), bb3));
+
+  // NCD when one block dominates the other is the dominator
+  assert(mlirBlockEqual(
+      mlirDominanceInfoFindNearestCommonDominator(cfgDomInfo, bb0, bb3), bb0));
+
+  // Reachable from entry
+  assert(mlirDominanceInfoIsReachableFromEntry(cfgDomInfo, bb3));
+
+  // Invalidate (no crash)
+  mlirDominanceInfoInvalidate(cfgDomInfo);
+
+  // PostDominanceInfo
+  MlirPostDominanceInfo postDomInfo = mlirPostDominanceInfoCreate(cfgFuncOp);
+
+  // bb3 post-dominates all other blocks (it's the merge point)
+  assert(mlirPostDominanceInfoPostDominatesBlock(postDomInfo, bb3, bb0));
+  assert(mlirPostDominanceInfoPostDominatesBlock(postDomInfo, bb3, bb1));
+  assert(mlirPostDominanceInfoPostDominatesBlock(postDomInfo, bb3, bb2));
+
+  // bb1 does not post-dominate bb0
+  assert(!mlirPostDominanceInfoPostDominatesBlock(postDomInfo, bb1, bb0));
+
+  // Self post-dominance vs proper
+  assert(mlirPostDominanceInfoPostDominatesBlock(postDomInfo, bb3, bb3));
+  assert(
+      !mlirPostDominanceInfoProperlyPostDominatesBlock(postDomInfo, bb3, bb3));
+
+  // Operation post-dominance within a single block
+  MlirOperation cfgC0Op = mlirBlockGetFirstOperation(bb0);
+  MlirOperation cfgC1Op = mlirOperationGetNextInBlock(cfgC0Op);
+  assert(mlirPostDominanceInfoProperlyPostDominatesOperation(postDomInfo,
+                                                             cfgC1Op, cfgC0Op));
+  assert(!mlirPostDominanceInfoProperlyPostDominatesOperation(
+      postDomInfo, cfgC0Op, cfgC1Op));
+
+  // PostDominates (includes self)
+  assert(mlirPostDominanceInfoPostDominatesOperation(postDomInfo, cfgC0Op,
+                                                     cfgC0Op));
+
+  // Invalidate (no crash)
+  mlirPostDominanceInfoInvalidate(postDomInfo);
+
+  mlirPostDominanceInfoDestroy(postDomInfo);
+  mlirDominanceInfoDestroy(cfgDomInfo);
+  mlirModuleDestroy(cfgModule);
+
+  // CHECK: testDominanceInfo: PASSED
+  fprintf(stderr, "testDominanceInfo: PASSED\n");
+  return 0;
+}
+
 int main(void) {
   MlirContext ctx = mlirContextCreate();
   registerAllUpstreamDialects(ctx);
@@ -2795,6 +2953,8 @@ int main(void) {
     return 18;
   if (testIRMapping(ctx))
     return 19;
+  if (testDominanceInfo(ctx))
+    return 20;
 
   // CHECK: DESTROY MAIN CONTEXT
   // CHECK: reportResourceDelete: resource_i64_blob



More information about the Mlir-commits mailing list