[Mlir-commits] [mlir] de2ba06 - [mlir-c] Add DominanceInfo and PostDominanceInfo C API bindings (#206156)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sun Jun 28 20:45:12 PDT 2026
Author: Maksim Levental
Date: 2026-06-28T20:45:08-07:00
New Revision: de2ba06fbde809e2e121d4ea63161f05a5bb56ba
URL: https://github.com/llvm/llvm-project/commit/de2ba06fbde809e2e121d4ea63161f05a5bb56ba
DIFF: https://github.com/llvm/llvm-project/commit/de2ba06fbde809e2e121d4ea63161f05a5bb56ba.diff
LOG: [mlir-c] Add DominanceInfo and PostDominanceInfo C API bindings (#206156)
Expose DominanceInfo and PostDominanceInfo through the MLIR C API with
operation/block/value dominance queries, nearest common dominator,
reachability, and invalidation.
Assisted by: Claude
Added:
mlir/include/mlir-c/Dominance.h
mlir/include/mlir/CAPI/Dominance.h
mlir/lib/CAPI/IR/Dominance.cpp
Modified:
mlir/lib/CAPI/IR/CMakeLists.txt
mlir/test/CAPI/ir.c
Removed:
################################################################################
diff --git a/mlir/include/mlir-c/Dominance.h b/mlir/include/mlir-c/Dominance.h
new file mode 100644
index 0000000000000..0923e227c0b34
--- /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