[Mlir-commits] [mlir] [mlir-c] Add RegionBranchOpInterface C API bindings (PR #206563)
Maksim Levental
llvmlistbot at llvm.org
Mon Jun 29 11:51:56 PDT 2026
https://github.com/makslevental updated https://github.com/llvm/llvm-project/pull/206563
>From 9404cc4460e586bdde60c7ad4c19a5889080d244 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Mon, 29 Jun 2026 11:38:45 -0700
Subject: [PATCH] [mlir-c] Add RegionBranchOpInterface C API bindings
---
mlir/include/mlir-c/Interfaces.h | 34 +++++++++++++
mlir/lib/CAPI/Interfaces/CMakeLists.txt | 1 +
mlir/lib/CAPI/Interfaces/Interfaces.cpp | 42 +++++++++++++++
mlir/test/CAPI/ir.c | 68 +++++++++++++++++++++++++
4 files changed, 145 insertions(+)
diff --git a/mlir/include/mlir-c/Interfaces.h b/mlir/include/mlir-c/Interfaces.h
index a5251dc78471f..4566fd419218c 100644
--- a/mlir/include/mlir-c/Interfaces.h
+++ b/mlir/include/mlir-c/Interfaces.h
@@ -165,6 +165,40 @@ MLIR_CAPI_EXPORTED void mlirMemoryEffectsOpInterfaceAttachFallbackModel(
MlirContext ctx, MlirStringRef opName,
MlirMemoryEffectsOpInterfaceCallbacks callbacks);
+//===---------------------------------------------------------------------===//
+// RegionBranchOpInterface
+//===---------------------------------------------------------------------===//
+
+/// Returns the interface TypeID of the RegionBranchOpInterface.
+MLIR_CAPI_EXPORTED MlirTypeID mlirRegionBranchOpInterfaceTypeID(void);
+
+/// Returns the regions that control can branch into when entering the given
+/// region-branch operation from its parent. The successor regions are written
+/// into the caller-allocated `regions` buffer, up to `n` entries, and the total
+/// number of entry successors is returned. A null MlirRegion entry denotes a
+/// successor that leaves the operation (i.e. the parent op itself). The
+/// operation must implement RegionBranchOpInterface.
+MLIR_CAPI_EXPORTED intptr_t mlirRegionBranchOpInterfaceGetEntrySuccessorRegions(
+ MlirOperation op, intptr_t n, MlirRegion *regions);
+
+/// Returns the regions that control can branch into from the given region
+/// terminator. `terminator` must be a terminator operation (implementing
+/// RegionBranchTerminatorOpInterface) nested in one of `op`'s regions. The
+/// successor regions are written into the caller-allocated `regions` buffer, up
+/// to `n` entries, and the total number of successors is returned. A null
+/// MlirRegion entry denotes a successor that leaves the operation (i.e. the
+/// parent op itself). The operation must implement RegionBranchOpInterface.
+MLIR_CAPI_EXPORTED intptr_t mlirRegionBranchOpInterfaceGetSuccessorRegions(
+ MlirOperation op, MlirOperation terminator, intptr_t n,
+ MlirRegion *regions);
+
+/// Returns true if the types are compatible for region branching between the
+/// successor regions of the given operation. The operation must implement
+/// RegionBranchOpInterface.
+MLIR_CAPI_EXPORTED bool
+mlirRegionBranchOpInterfaceAreTypesCompatible(MlirOperation op, MlirType lhs,
+ MlirType rhs);
+
#ifdef __cplusplus
}
#endif
diff --git a/mlir/lib/CAPI/Interfaces/CMakeLists.txt b/mlir/lib/CAPI/Interfaces/CMakeLists.txt
index dbe3f33975980..d5a711404e808 100644
--- a/mlir/lib/CAPI/Interfaces/CMakeLists.txt
+++ b/mlir/lib/CAPI/Interfaces/CMakeLists.txt
@@ -2,5 +2,6 @@ add_mlir_upstream_c_api_library(MLIRCAPIInterfaces
Interfaces.cpp
LINK_LIBS PUBLIC
+ MLIRControlFlowInterfaces
MLIRInferTypeOpInterface
MLIRSideEffectInterfaces)
diff --git a/mlir/lib/CAPI/Interfaces/Interfaces.cpp b/mlir/lib/CAPI/Interfaces/Interfaces.cpp
index 35a2bd562a8a1..463d0cef684d3 100644
--- a/mlir/lib/CAPI/Interfaces/Interfaces.cpp
+++ b/mlir/lib/CAPI/Interfaces/Interfaces.cpp
@@ -15,6 +15,7 @@
#include "mlir/CAPI/Support.h"
#include "mlir/CAPI/Wrap.h"
#include "mlir/IR/ValueRange.h"
+#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/InferTypeOpInterface.h"
#include "llvm/ADT/ScopeExit.h"
#include <optional>
@@ -345,3 +346,44 @@ void mlirMemoryEffectsOpInterfaceAttachFallbackModel(
assert(model && "Failed to get MemoryEffectOpInterfaceFallbackModel");
model->setCallbacks(callbacks);
}
+
+//===---------------------------------------------------------------------===//
+// RegionBranchOpInterface
+//===---------------------------------------------------------------------===//
+
+MlirTypeID mlirRegionBranchOpInterfaceTypeID(void) {
+ return wrap(RegionBranchOpInterface::getInterfaceID());
+}
+
+intptr_t mlirRegionBranchOpInterfaceGetEntrySuccessorRegions(
+ MlirOperation op, intptr_t n, MlirRegion *regions) {
+ auto branchOp = cast<RegionBranchOpInterface>(unwrap(op));
+ SmallVector<RegionSuccessor> successors;
+ branchOp.getSuccessorRegions(RegionBranchPoint::parent(), successors);
+ intptr_t count = static_cast<intptr_t>(successors.size());
+ for (intptr_t i = 0; i < count && i < n; ++i)
+ // A null successor region denotes the parent op (control leaves the op).
+ regions[i] = wrap(successors[i].getSuccessor());
+ return count;
+}
+
+intptr_t mlirRegionBranchOpInterfaceGetSuccessorRegions(
+ MlirOperation op, MlirOperation terminator, intptr_t n,
+ MlirRegion *regions) {
+ auto branchOp = cast<RegionBranchOpInterface>(unwrap(op));
+ RegionBranchPoint point(
+ cast<RegionBranchTerminatorOpInterface>(unwrap(terminator)));
+ SmallVector<RegionSuccessor> successors;
+ branchOp.getSuccessorRegions(point, successors);
+ intptr_t count = static_cast<intptr_t>(successors.size());
+ for (intptr_t i = 0; i < count && i < n; ++i)
+ // A null successor region denotes the parent op (control leaves the op).
+ regions[i] = wrap(successors[i].getSuccessor());
+ return count;
+}
+
+bool mlirRegionBranchOpInterfaceAreTypesCompatible(MlirOperation op,
+ MlirType lhs, MlirType rhs) {
+ return cast<RegionBranchOpInterface>(unwrap(op))
+ .areTypesCompatible(unwrap(lhs), unwrap(rhs));
+}
diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c
index 57ae8b9a2819b..f321f32dca063 100644
--- a/mlir/test/CAPI/ir.c
+++ b/mlir/test/CAPI/ir.c
@@ -2900,6 +2900,72 @@ int testDominanceInfo(MlirContext ctx) {
return 0;
}
+int testRegionBranchOpInterface(MlirContext ctx) {
+ fprintf(stderr, "@testRegionBranchOpInterface\n");
+ // CHECK-LABEL: @testRegionBranchOpInterface
+
+ mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("scf"));
+
+ const char *moduleStr = "func.func @f(%cond: i1) {\n"
+ " scf.if %cond {\n"
+ " scf.yield\n"
+ " } else {\n"
+ " scf.yield\n"
+ " }\n"
+ " return\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 ifOp = mlirBlockGetFirstOperation(funcBody);
+ MlirRegion thenRegion = mlirOperationGetRegion(ifOp, 0);
+ MlirRegion elseRegion = mlirOperationGetRegion(ifOp, 1);
+
+ // The scf.if op implements the interface.
+ MlirTypeID id = mlirRegionBranchOpInterfaceTypeID();
+ assert(!mlirTypeIDIsNull(id));
+ assert(mlirOperationImplementsInterface(ifOp, id));
+
+ // Entering the scf.if from its parent can branch into the then and the else
+ // regions.
+ MlirRegion successors[2];
+ intptr_t count =
+ mlirRegionBranchOpInterfaceGetEntrySuccessorRegions(ifOp, 2, successors);
+ assert(count == 2);
+ bool matchesInOrder = mlirRegionEqual(successors[0], thenRegion) &&
+ mlirRegionEqual(successors[1], elseRegion);
+ bool matchesSwapped = mlirRegionEqual(successors[0], elseRegion) &&
+ mlirRegionEqual(successors[1], thenRegion);
+ assert(matchesInOrder || matchesSwapped);
+
+ // Branching from a region's terminator (the scf.yield in the then region)
+ // leaves the op: the single successor is the parent, denoted by a null
+ // region.
+ MlirBlock thenBlock = mlirRegionGetFirstBlock(thenRegion);
+ MlirOperation thenYield = mlirBlockGetTerminator(thenBlock);
+ MlirRegion fromTerminator[1];
+ intptr_t fromCount = mlirRegionBranchOpInterfaceGetSuccessorRegions(
+ ifOp, thenYield, 1, fromTerminator);
+ assert(fromCount == 1);
+ assert(mlirRegionIsNull(fromTerminator[0]));
+
+ // Type compatibility along control flow uses equality by default.
+ MlirType i32 = mlirIntegerTypeGet(ctx, 32);
+ MlirType i64 = mlirIntegerTypeGet(ctx, 64);
+ assert(mlirRegionBranchOpInterfaceAreTypesCompatible(ifOp, i32, i32));
+ assert(!mlirRegionBranchOpInterfaceAreTypesCompatible(ifOp, i32, i64));
+
+ mlirModuleDestroy(module);
+
+ // CHECK: testRegionBranchOpInterface: PASSED
+ fprintf(stderr, "testRegionBranchOpInterface: PASSED\n");
+ return 0;
+}
+
int main(void) {
MlirContext ctx = mlirContextCreate();
registerAllUpstreamDialects(ctx);
@@ -2955,6 +3021,8 @@ int main(void) {
return 19;
if (testDominanceInfo(ctx))
return 20;
+ if (testRegionBranchOpInterface(ctx))
+ return 21;
// CHECK: DESTROY MAIN CONTEXT
// CHECK: reportResourceDelete: resource_i64_blob
More information about the Mlir-commits
mailing list