Rolf Morel a1d7cda1d7
[MLIR][Python] Impl XOpInterface(s) from Python, with X=Transform and X=MemoryEffects (#176920)
Provides the infrastructure for implementing and late-binding
OpInterfaces from Python.

* On the mlir-c API declaration side, each `XOpInterface` has a callback
struct, with a callback for each method and a userdata member (provided
as an arg to each method), and a
`mlirXOpInterfaceAttachFallbackModel(ctx, op_name, callbacks)` func.
* This CAPI is implemented by defining a subclass of
`XOpInterface::FallbackModel` that holds the callback struct and has
each method call the corresponding callback (with userdata as an arg).
Given a callback struct, a new `FallbackModel` is created and attached,
i.e. late bound, to the named op. (MLIR's interface infrastructure is
such that the thus registered `FallbackModel` will be returned in case
the op gets cast to the `XOpInterface`.)
* On the Python side, we expose a stand-in `XOpInterface` base class
which has one (class)method: `XOpInterface.attach(cls, op_name, ctx)`.
Python users subclass this class (`class MyInterfaceImpl(XOpInterface):
...`) and implement the interface's methods (with the right names and
signatures). The user calls `attach` on the subclass
(`MyInterfaceImpl.attach("my_dialect.my_op", ctx)`) which prepares the
callbacks struct _with userdata set to the subclass_ (as we use it to
lookup methods). These callbacks (and userdata) are then registered as
an `XOpInterface::FallbackModel` by
`mlirXOpInterfaceAttachFallbackModel(...)`. From then on the Python
methods will be used to respond to calls to the interface methods
(originating in C++).

This PR enables implementing the TransformOpInterface and the
MemoryEffectsOpInterface, both of which are required for making an op
into a transform op.

Everything besides the above linked code is there to facilitate exposing
the interfaces: the right types for the arguments of the methods are
exposed as are functions/methods for manipulating these arguments (e.g.
specifying side effects on `OpOperand`s and `OpResult`s and being able
to access and set the transform handles associated with args and
results).
2026-02-12 14:07:10 +00:00

84 lines
3.0 KiB
C++

//===- Rewrite.h - Rewrite Submodules of pybind module --------------------===//
//
// 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_BINDINGS_PYTHON_REWRITE_H
#define MLIR_BINDINGS_PYTHON_REWRITE_H
#include "mlir-c/Rewrite.h"
#include "mlir/Bindings/Python/IRCore.h"
#include <nanobind/nanobind.h>
namespace mlir {
namespace python {
namespace MLIR_BINDINGS_PYTHON_DOMAIN {
/// CRTP Base class for rewriter wrappers.
template <typename DerivedTy>
class MLIR_PYTHON_API_EXPORTED PyRewriterBase {
public:
PyRewriterBase(MlirRewriterBase rewriter)
: base(rewriter),
ctx(PyMlirContext::forContext(mlirRewriterBaseGetContext(base))) {}
PyInsertionPoint getInsertionPoint() const {
MlirBlock block = mlirRewriterBaseGetInsertionBlock(base);
MlirOperation op = mlirRewriterBaseGetOperationAfterInsertion(base);
if (mlirOperationIsNull(op)) {
MlirOperation owner = mlirBlockGetParentOperation(block);
auto parent = PyOperation::forOperation(ctx, owner);
return PyInsertionPoint(PyBlock(parent, block));
}
return PyInsertionPoint(PyOperation::forOperation(ctx, op));
}
static void bind(nanobind::module_ &m) {
nanobind::class_<DerivedTy>(m, DerivedTy::pyClassName)
.def_prop_ro("ip", &PyRewriterBase::getInsertionPoint,
"The current insertion point of the PatternRewriter.")
.def(
"replace_op",
[](DerivedTy &self, PyOperationBase &op, PyOperationBase &newOp) {
mlirRewriterBaseReplaceOpWithOperation(
self.base, op.getOperation(), newOp.getOperation());
},
"Replace an operation with a new operation.", nanobind::arg("op"),
nanobind::arg("new_op"))
.def(
"replace_op",
[](DerivedTy &self, PyOperationBase &op,
const std::vector<PyValue> &values) {
std::vector<MlirValue> values_(values.size());
std::copy(values.begin(), values.end(), values_.begin());
mlirRewriterBaseReplaceOpWithValues(
self.base, op.getOperation(), values_.size(), values_.data());
},
"Replace an operation with a list of values.", nanobind::arg("op"),
nanobind::arg("values"))
.def(
"erase_op",
[](DerivedTy &self, PyOperationBase &op) {
mlirRewriterBaseEraseOp(self.base, op.getOperation());
},
"Erase an operation.", nanobind::arg("op"));
}
private:
MlirRewriterBase base;
PyMlirContextRef ctx;
};
void MLIR_PYTHON_API_EXPORTED populateRewriteSubmodule(nanobind::module_ &m);
} // namespace MLIR_BINDINGS_PYTHON_DOMAIN
} // namespace python
} // namespace mlir
#endif // MLIR_BINDINGS_PYTHON_REWRITE_H