[Mlir-commits] [mlir] [mlir-c] Add LoopLikeOpInterface C API bindings (PR #206560)
Maksim Levental
llvmlistbot at llvm.org
Mon Jun 29 11:51:47 PDT 2026
https://github.com/makslevental updated https://github.com/llvm/llvm-project/pull/206560
>From aa97fe4dc6d7248c975f13abd7ad7d5b77410533 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Mon, 29 Jun 2026 11:30:14 -0700
Subject: [PATCH] [mlir-c] Add LoopLikeOpInterface C API bindings
---
mlir/include/mlir-c/Interfaces.h | 45 ++++++++++++++
mlir/lib/CAPI/Interfaces/CMakeLists.txt | 1 +
mlir/lib/CAPI/Interfaces/Interfaces.cpp | 61 +++++++++++++++++++
mlir/test/CAPI/ir.c | 81 +++++++++++++++++++++++++
4 files changed, 188 insertions(+)
diff --git a/mlir/include/mlir-c/Interfaces.h b/mlir/include/mlir-c/Interfaces.h
index a5251dc78471f..f369f948b0cdf 100644
--- a/mlir/include/mlir-c/Interfaces.h
+++ b/mlir/include/mlir-c/Interfaces.h
@@ -165,6 +165,51 @@ MLIR_CAPI_EXPORTED void mlirMemoryEffectsOpInterfaceAttachFallbackModel(
MlirContext ctx, MlirStringRef opName,
MlirMemoryEffectsOpInterfaceCallbacks callbacks);
+//===---------------------------------------------------------------------===//
+// LoopLikeOpInterface
+//===---------------------------------------------------------------------===//
+
+/// Returns the interface TypeID of the LoopLikeOpInterface.
+MLIR_CAPI_EXPORTED MlirTypeID mlirLoopLikeOpInterfaceTypeID(void);
+
+/// Returns the loop regions of the given loop-like operation. The regions are
+/// written into the caller-allocated `regions` buffer, up to `n` entries, and
+/// the total number of loop regions is returned. The operation must implement
+/// LoopLikeOpInterface.
+MLIR_CAPI_EXPORTED intptr_t mlirLoopLikeOpInterfaceGetLoopRegions(
+ MlirOperation op, intptr_t n, MlirRegion *regions);
+
+/// Returns true if the given value is defined outside of the loop. The
+/// operation must implement LoopLikeOpInterface.
+MLIR_CAPI_EXPORTED bool
+mlirLoopLikeOpInterfaceIsDefinedOutsideOfLoop(MlirOperation op,
+ MlirValue value);
+
+/// Returns the induction variables of the loop. The values are written into the
+/// caller-allocated `vars` buffer, up to `n` entries, and the total number of
+/// induction variables is returned. A negative return value indicates that the
+/// loop has no notion of induction variables. The operation must implement
+/// LoopLikeOpInterface.
+MLIR_CAPI_EXPORTED intptr_t mlirLoopLikeOpInterfaceGetLoopInductionVars(
+ MlirOperation op, intptr_t n, MlirValue *vars);
+
+/// Returns the region iter_args of the loop (the block arguments corresponding
+/// to the init operands). The values are written into the caller-allocated
+/// `args` buffer, up to `n` entries, and the total number is returned. The
+/// operation must implement LoopLikeOpInterface.
+MLIR_CAPI_EXPORTED intptr_t mlirLoopLikeOpInterfaceGetRegionIterArgs(
+ MlirOperation op, intptr_t n, MlirValue *args);
+
+/// Moves the given loop-invariant operation out of the loop. The loop operation
+/// must implement LoopLikeOpInterface.
+MLIR_CAPI_EXPORTED void
+mlirLoopLikeOpInterfaceMoveOutOfLoop(MlirOperation loopOp, MlirOperation op);
+
+/// Returns the static trip count of the loop, or -1 if it cannot be determined
+/// statically. The operation must implement LoopLikeOpInterface.
+MLIR_CAPI_EXPORTED int64_t
+mlirLoopLikeOpInterfaceGetStaticTripCount(MlirOperation op);
+
#ifdef __cplusplus
}
#endif
diff --git a/mlir/lib/CAPI/Interfaces/CMakeLists.txt b/mlir/lib/CAPI/Interfaces/CMakeLists.txt
index dbe3f33975980..3bf07011ed25c 100644
--- a/mlir/lib/CAPI/Interfaces/CMakeLists.txt
+++ b/mlir/lib/CAPI/Interfaces/CMakeLists.txt
@@ -3,4 +3,5 @@ add_mlir_upstream_c_api_library(MLIRCAPIInterfaces
LINK_LIBS PUBLIC
MLIRInferTypeOpInterface
+ MLIRLoopLikeInterface
MLIRSideEffectInterfaces)
diff --git a/mlir/lib/CAPI/Interfaces/Interfaces.cpp b/mlir/lib/CAPI/Interfaces/Interfaces.cpp
index 35a2bd562a8a1..8d18cd9a6ed4f 100644
--- a/mlir/lib/CAPI/Interfaces/Interfaces.cpp
+++ b/mlir/lib/CAPI/Interfaces/Interfaces.cpp
@@ -16,6 +16,7 @@
#include "mlir/CAPI/Wrap.h"
#include "mlir/IR/ValueRange.h"
#include "mlir/Interfaces/InferTypeOpInterface.h"
+#include "mlir/Interfaces/LoopLikeInterface.h"
#include "llvm/ADT/ScopeExit.h"
#include <optional>
@@ -345,3 +346,63 @@ void mlirMemoryEffectsOpInterfaceAttachFallbackModel(
assert(model && "Failed to get MemoryEffectOpInterfaceFallbackModel");
model->setCallbacks(callbacks);
}
+
+//===---------------------------------------------------------------------===//
+// LoopLikeOpInterface
+//===---------------------------------------------------------------------===//
+
+MlirTypeID mlirLoopLikeOpInterfaceTypeID(void) {
+ return wrap(LoopLikeOpInterface::getInterfaceID());
+}
+
+intptr_t mlirLoopLikeOpInterfaceGetLoopRegions(MlirOperation op, intptr_t n,
+ MlirRegion *regions) {
+ SmallVector<Region *> loopRegions =
+ cast<LoopLikeOpInterface>(unwrap(op)).getLoopRegions();
+ intptr_t count = static_cast<intptr_t>(loopRegions.size());
+ for (intptr_t i = 0; i < count && i < n; ++i)
+ regions[i] = wrap(loopRegions[i]);
+ return count;
+}
+
+bool mlirLoopLikeOpInterfaceIsDefinedOutsideOfLoop(MlirOperation op,
+ MlirValue value) {
+ return cast<LoopLikeOpInterface>(unwrap(op))
+ .isDefinedOutsideOfLoop(unwrap(value));
+}
+
+intptr_t mlirLoopLikeOpInterfaceGetLoopInductionVars(MlirOperation op,
+ intptr_t n,
+ MlirValue *vars) {
+ std::optional<SmallVector<Value>> inductionVars =
+ cast<LoopLikeOpInterface>(unwrap(op)).getLoopInductionVars();
+ if (!inductionVars)
+ return -1;
+ intptr_t count = static_cast<intptr_t>(inductionVars->size());
+ for (intptr_t i = 0; i < count && i < n; ++i)
+ vars[i] = wrap((*inductionVars)[i]);
+ return count;
+}
+
+intptr_t mlirLoopLikeOpInterfaceGetRegionIterArgs(MlirOperation op, intptr_t n,
+ MlirValue *args) {
+ Block::BlockArgListType iterArgs =
+ cast<LoopLikeOpInterface>(unwrap(op)).getRegionIterArgs();
+ intptr_t count = static_cast<intptr_t>(iterArgs.size());
+ for (intptr_t i = 0; i < count && i < n; ++i)
+ args[i] = wrap(iterArgs[i]);
+ return count;
+}
+
+void mlirLoopLikeOpInterfaceMoveOutOfLoop(MlirOperation loopOp,
+ MlirOperation op) {
+ cast<LoopLikeOpInterface>(unwrap(loopOp)).moveOutOfLoop(unwrap(op));
+}
+
+int64_t mlirLoopLikeOpInterfaceGetStaticTripCount(MlirOperation op) {
+ std::optional<llvm::APInt> tripCount =
+ cast<LoopLikeOpInterface>(unwrap(op)).getStaticTripCount();
+ if (!tripCount)
+ return -1;
+ return static_cast<int64_t>(tripCount->getZExtValue());
+}
diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c
index 57ae8b9a2819b..70bc2d9c6106a 100644
--- a/mlir/test/CAPI/ir.c
+++ b/mlir/test/CAPI/ir.c
@@ -2900,6 +2900,85 @@ int testDominanceInfo(MlirContext ctx) {
return 0;
}
+int testLoopLikeOpInterface(MlirContext ctx) {
+ fprintf(stderr, "@testLoopLikeOpInterface\n");
+ // CHECK-LABEL: @testLoopLikeOpInterface
+
+ mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("arith"));
+ mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("scf"));
+
+ const char *moduleStr =
+ "func.func @f(%arg0: i32) -> i32 {\n"
+ " %lb = arith.constant 0 : index\n"
+ " %ub = arith.constant 10 : index\n"
+ " %step = arith.constant 1 : index\n"
+ " %0 = scf.for %i = %lb to %ub step %step iter_args(%acc = %arg0) -> "
+ "(i32) {\n"
+ " %inv = arith.muli %arg0, %arg0 : i32\n"
+ " %1 = arith.addi %acc, %inv : i32\n"
+ " scf.yield %1 : i32\n"
+ " }\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);
+ MlirValue funcArg0 = mlirBlockGetArgument(funcBody, 0);
+
+ MlirOperation lbOp = mlirBlockGetFirstOperation(funcBody);
+ MlirOperation ubOp = mlirOperationGetNextInBlock(lbOp);
+ MlirOperation stepOp = mlirOperationGetNextInBlock(ubOp);
+ MlirOperation forOp = mlirOperationGetNextInBlock(stepOp);
+
+ MlirRegion forRegion = mlirOperationGetRegion(forOp, 0);
+ MlirBlock forBody = mlirRegionGetFirstBlock(forRegion);
+ MlirValue indVar = mlirBlockGetArgument(forBody, 0);
+ MlirValue iterArg = mlirBlockGetArgument(forBody, 1);
+
+ // The scf.for op implements the interface.
+ MlirTypeID id = mlirLoopLikeOpInterfaceTypeID();
+ assert(!mlirTypeIDIsNull(id));
+ assert(mlirOperationImplementsInterface(forOp, id));
+
+ // It has a single loop region, which is the body region.
+ MlirRegion regions[1];
+ assert(mlirLoopLikeOpInterfaceGetLoopRegions(forOp, 1, regions) == 1);
+ assert(mlirRegionEqual(regions[0], forRegion));
+
+ // The function argument is defined outside the loop; the iter_arg is not.
+ assert(mlirLoopLikeOpInterfaceIsDefinedOutsideOfLoop(forOp, funcArg0));
+ assert(!mlirLoopLikeOpInterfaceIsDefinedOutsideOfLoop(forOp, iterArg));
+
+ // The single induction variable is the loop body's first block argument.
+ MlirValue vars[1];
+ assert(mlirLoopLikeOpInterfaceGetLoopInductionVars(forOp, 1, vars) == 1);
+ assert(mlirValueEqual(vars[0], indVar));
+
+ // The single region iter_arg is the loop body's second block argument.
+ MlirValue iterArgs[1];
+ assert(mlirLoopLikeOpInterfaceGetRegionIterArgs(forOp, 1, iterArgs) == 1);
+ assert(mlirValueEqual(iterArgs[0], iterArg));
+
+ // lb = 0, ub = 10, step = 1 -> static trip count of 10.
+ assert(mlirLoopLikeOpInterfaceGetStaticTripCount(forOp) == 10);
+
+ // Move the loop-invariant `arith.muli` out of the loop; it should land in the
+ // function body alongside the for op.
+ MlirOperation invOp = mlirBlockGetFirstOperation(forBody);
+ mlirLoopLikeOpInterfaceMoveOutOfLoop(forOp, invOp);
+ assert(mlirBlockEqual(mlirOperationGetBlock(invOp), funcBody));
+
+ mlirModuleDestroy(module);
+
+ // CHECK: testLoopLikeOpInterface: PASSED
+ fprintf(stderr, "testLoopLikeOpInterface: PASSED\n");
+ return 0;
+}
+
int main(void) {
MlirContext ctx = mlirContextCreate();
registerAllUpstreamDialects(ctx);
@@ -2955,6 +3034,8 @@ int main(void) {
return 19;
if (testDominanceInfo(ctx))
return 20;
+ if (testLoopLikeOpInterface(ctx))
+ return 21;
// CHECK: DESTROY MAIN CONTEXT
// CHECK: reportResourceDelete: resource_i64_blob
More information about the Mlir-commits
mailing list