MLIR 24.0.0git
NanobindAdaptors.h
Go to the documentation of this file.
1//===- NanobindAdaptors.h - Interop with MLIR APIs via nanobind -----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// This file contains adaptors for clients of the core MLIR Python APIs to
9// interop via MLIR CAPI types, using nanobind. The facilities here do not
10// depend on implementation details of the MLIR Python API and do not introduce
11// C++-level dependencies with it (requiring only Python and CAPI-level
12// dependencies).
13//
14// It is encouraged to be used both in-tree and out-of-tree. For in-tree use
15// cases, it should be used for dialect implementations (versus relying on
16// Pybind-based internals of the core libraries).
17//===----------------------------------------------------------------------===//
18
19#ifndef MLIR_BINDINGS_PYTHON_NANOBINDADAPTORS_H
20#define MLIR_BINDINGS_PYTHON_NANOBINDADAPTORS_H
21
22#include <cstdint>
23#include <memory>
24#include <optional>
25
26#include "mlir-c/Diagnostics.h"
27#include "mlir-c/IR.h"
28// clang-format off
30#include "mlir-c/Bindings/Python/Interop.h" // This is expected after nanobind.
31// clang-format on
33
34namespace mlir {
35namespace python {
36namespace {
37
38nanobind::module_ &irModule() {
39 static SafeInit<nanobind::module_> init([]() {
40 return std::make_unique<nanobind::module_>(
41 nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir")));
42 });
43 return init.get();
44}
45
46} // namespace
47} // namespace python
48} // namespace mlir
49
50// Raw CAPI type casters need to be declared before use, so always include them
51// first.
52namespace nanobind {
53namespace detail {
54
55/// Helper to convert a presumed MLIR API object to a capsule, accepting either
56/// an explicit Capsule (which can happen when two C APIs are communicating
57/// directly via Python) or indirectly by querying the MLIR_PYTHON_CAPI_PTR_ATTR
58/// attribute (through which supported MLIR Python API objects export their
59/// contained API pointer as a capsule). Throws a type error if the object is
60/// neither. This is intended to be used from type casters, which are invoked
61/// with a raw handle (unowned). The returned object's lifetime may not extend
62/// beyond the apiObject handle without explicitly having its refcount increased
63/// (i.e. on return).
64static std::optional<nanobind::object>
65mlirApiObjectToCapsule(nanobind::handle apiObject) {
66 if (PyCapsule_CheckExact(apiObject.ptr()))
67 return nanobind::borrow<nanobind::object>(apiObject);
68 nanobind::object api =
69 nanobind::getattr(apiObject, MLIR_PYTHON_CAPI_PTR_ATTR, nanobind::none());
70 if (api.is_none())
71 return {};
72 return api;
73}
74
75/// Clears the Python error indicator if the given condition `val` is false and
76/// returns the condition. This is needed in `from_python` of the type casters
77/// below, where a failed conversion from a Python capsule sets the Python error
78/// indicator but the caller of the caster expects or may even need a clean
79/// error indicator.
80inline bool pyErrClearIfFalse(bool val) {
81 if (!val)
82 PyErr_Clear();
83 return val;
84}
85
86/// Invokes `f`, which creates the Python object for a `from_cpp` conversion
87/// below. `from_cpp` is `noexcept`, but creating the object calls into Python
88/// (e.g. `maybe_downcast` runs user-registered type/value casters), which may
89/// raise. Letting the resulting `nanobind::python_error` escape would call
90/// `std::terminate`. Instead, restore it as the Python error indicator and
91/// return an invalid handle, which nanobind propagates to the Python caller.
92template <typename F>
93static handle mlirFromCppNoThrow(F &&f) noexcept {
94 try {
95 return f();
96 } catch (python_error &e) {
97 e.restore();
98 return handle();
99 }
100}
101
102// Note: Currently all of the following support cast from nanobind::object to
103// the Mlir* C-API type, but only a few light-weight, context-bound ones
104// implicitly cast the other way because the use case has not yet emerged and
105// ownership is unclear.
106
107/// Casts object <-> MlirAffineMap.
108template <>
109struct type_caster<MlirAffineMap> {
110 NB_TYPE_CASTER(MlirAffineMap,
111 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.AffineMap")))
112 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
113 if (auto capsule = mlirApiObjectToCapsule(src)) {
114 value = mlirPythonCapsuleToAffineMap(capsule->ptr());
116 }
117 return false;
118 }
119 static handle from_cpp(MlirAffineMap v, rv_policy,
120 cleanup_list *cleanup) noexcept {
121 return mlirFromCppNoThrow([&]() -> handle {
122 nanobind::object capsule =
123 nanobind::steal<nanobind::object>(mlirPythonAffineMapToCapsule(v));
124 return mlir::python::irModule()
125 .attr("AffineMap")
126 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
127 .release();
128 });
129 }
130};
131
132/// Casts object <-> MlirAttribute.
133template <>
134struct type_caster<MlirAttribute> {
135 NB_TYPE_CASTER(MlirAttribute,
136 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Attribute")))
137 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
138 if (auto capsule = mlirApiObjectToCapsule(src)) {
139 value = mlirPythonCapsuleToAttribute(capsule->ptr());
140 return pyErrClearIfFalse(!mlirAttributeIsNull(value));
141 }
142 return false;
143 }
144 static handle from_cpp(MlirAttribute v, rv_policy,
145 cleanup_list *cleanup) noexcept {
146 return mlirFromCppNoThrow([&]() -> handle {
147 nanobind::object capsule =
148 nanobind::steal<nanobind::object>(mlirPythonAttributeToCapsule(v));
149 return mlir::python::irModule()
150 .attr("Attribute")
151 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
153 .release();
154 });
155 }
156};
157
158/// Casts object -> MlirBlock.
159template <>
160struct type_caster<MlirBlock> {
161 NB_TYPE_CASTER(MlirBlock, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Block")))
162 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
163 if (auto capsule = mlirApiObjectToCapsule(src)) {
164 value = mlirPythonCapsuleToBlock(capsule->ptr());
165 return pyErrClearIfFalse(!mlirBlockIsNull(value));
166 }
167 return false;
168 }
169};
170
171/// Casts object -> MlirContext.
172template <>
173struct type_caster<MlirContext> {
174 NB_TYPE_CASTER(MlirContext,
175 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Context")))
176 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
177 if (src.is_none()) {
178 // Gets the current thread-bound context.
179 src = mlir::python::irModule().attr("Context").attr("current");
180 }
181 // If there is no context, including thread-bound, emit a warning (since
182 // this function is not allowed to throw) and fail to cast.
183 if (src.is_none()) {
184 PyErr_WarnEx(
185 PyExc_RuntimeWarning,
186 "Passing None as MLIR Context is only allowed inside "
187 "the " MAKE_MLIR_PYTHON_QUALNAME("ir.Context") " context manager.",
188 /*stacklevel=*/1);
189 return false;
190 }
191 if (std::optional<nanobind::object> capsule = mlirApiObjectToCapsule(src)) {
192 value = mlirPythonCapsuleToContext(capsule->ptr());
193 return pyErrClearIfFalse(!mlirContextIsNull(value));
194 }
195 return false;
196 }
197};
198
199/// Casts object <-> MlirDialectRegistry.
200template <>
201struct type_caster<MlirDialectRegistry> {
202 NB_TYPE_CASTER(MlirDialectRegistry,
203 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.DialectRegistry")))
204 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
205 if (auto capsule = mlirApiObjectToCapsule(src)) {
206 value = mlirPythonCapsuleToDialectRegistry(capsule->ptr());
208 }
209 return false;
210 }
211 static handle from_cpp(MlirDialectRegistry v, rv_policy,
212 cleanup_list *cleanup) noexcept {
213 return mlirFromCppNoThrow([&]() -> handle {
214 nanobind::object capsule = nanobind::steal<nanobind::object>(
216 return mlir::python::irModule()
217 .attr("DialectRegistry")
218 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
219 .release();
220 });
221 }
222};
223
224/// Casts object <-> MlirLocation.
225template <>
226struct type_caster<MlirLocation> {
227 NB_TYPE_CASTER(MlirLocation,
228 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Location")))
229 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
230 if (src.is_none()) {
231 // Gets the current thread-bound context.
232 src = mlir::python::irModule().attr("Location").attr("current");
233 }
234 if (auto capsule = mlirApiObjectToCapsule(src)) {
235 value = mlirPythonCapsuleToLocation(capsule->ptr());
237 }
238 return false;
239 }
240 static handle from_cpp(MlirLocation v, rv_policy,
241 cleanup_list *cleanup) noexcept {
242 return mlirFromCppNoThrow([&]() -> handle {
243 nanobind::object capsule =
244 nanobind::steal<nanobind::object>(mlirPythonLocationToCapsule(v));
245 return mlir::python::irModule()
246 .attr("Location")
247 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
248 .release();
249 });
250 }
251};
252
253/// Casts object <-> MlirModule.
254template <>
255struct type_caster<MlirModule> {
256 NB_TYPE_CASTER(MlirModule, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Module")))
257 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
258 if (auto capsule = mlirApiObjectToCapsule(src)) {
259 value = mlirPythonCapsuleToModule(capsule->ptr());
260 return pyErrClearIfFalse(!mlirModuleIsNull(value));
261 }
262 return false;
263 }
264 static handle from_cpp(MlirModule v, rv_policy,
265 cleanup_list *cleanup) noexcept {
266 return mlirFromCppNoThrow([&]() -> handle {
267 nanobind::object capsule =
268 nanobind::steal<nanobind::object>(mlirPythonModuleToCapsule(v));
269 return mlir::python::irModule()
270 .attr("Module")
271 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
272 .release();
273 });
274 };
275};
276
277/// Casts object <-> MlirFrozenRewritePatternSet.
278template <>
279struct type_caster<MlirFrozenRewritePatternSet> {
281 MlirFrozenRewritePatternSet,
282 const_name(MAKE_MLIR_PYTHON_QUALNAME("rewrite.FrozenRewritePatternSet")))
283 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
284 if (auto capsule = mlirApiObjectToCapsule(src)) {
285 value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule->ptr());
286 return pyErrClearIfFalse(value.ptr != nullptr);
287 }
288 return false;
289 }
290 static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy,
291 handle) noexcept {
292 return mlirFromCppNoThrow([&]() -> handle {
293 nanobind::object capsule = nanobind::steal<nanobind::object>(
295 return nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("rewrite"))
296 .attr("FrozenRewritePatternSet")
297 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
298 .release();
299 });
300 };
301};
302
303/// Casts object <-> MlirOperation.
304template <>
305struct type_caster<MlirOperation> {
306 NB_TYPE_CASTER(MlirOperation,
307 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Operation")))
308 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
309 if (auto capsule = mlirApiObjectToCapsule(src)) {
310 value = mlirPythonCapsuleToOperation(capsule->ptr());
311 return pyErrClearIfFalse(!mlirOperationIsNull(value));
312 }
313 return false;
314 }
315 static handle from_cpp(MlirOperation v, rv_policy,
316 cleanup_list *cleanup) noexcept {
317 if (v.ptr == nullptr)
318 return nanobind::none();
319 return mlirFromCppNoThrow([&]() -> handle {
320 nanobind::object capsule =
321 nanobind::steal<nanobind::object>(mlirPythonOperationToCapsule(v));
322 return mlir::python::irModule()
323 .attr("Operation")
324 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
325 .release();
326 });
327 };
328};
329
330/// Casts object <-> MlirValue.
331template <>
332struct type_caster<MlirValue> {
333 NB_TYPE_CASTER(MlirValue, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Value")))
334 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
335 if (auto capsule = mlirApiObjectToCapsule(src)) {
336 value = mlirPythonCapsuleToValue(capsule->ptr());
337 return pyErrClearIfFalse(!mlirValueIsNull(value));
338 }
339 return false;
340 }
341 static handle from_cpp(MlirValue v, rv_policy,
342 cleanup_list *cleanup) noexcept {
343 if (v.ptr == nullptr)
344 return nanobind::none();
345 return mlirFromCppNoThrow([&]() -> handle {
346 nanobind::object capsule =
347 nanobind::steal<nanobind::object>(mlirPythonValueToCapsule(v));
348 return mlir::python::irModule()
349 .attr("Value")
350 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
352 .release();
353 });
354 };
355};
356
357/// Casts object -> MlirPassManager.
358template <>
359struct type_caster<MlirPassManager> {
360 NB_TYPE_CASTER(MlirPassManager, const_name(MAKE_MLIR_PYTHON_QUALNAME(
361 "passmanager.PassManager")))
362 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
363 if (auto capsule = mlirApiObjectToCapsule(src)) {
364 value = mlirPythonCapsuleToPassManager(capsule->ptr());
366 }
367 return false;
368 }
369};
370
371/// Casts object <-> MlirTypeID.
372template <>
373struct type_caster<MlirTypeID> {
374 NB_TYPE_CASTER(MlirTypeID, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID")))
375 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
376 if (auto capsule = mlirApiObjectToCapsule(src)) {
377 value = mlirPythonCapsuleToTypeID(capsule->ptr());
378 return pyErrClearIfFalse(!mlirTypeIDIsNull(value));
379 }
380 return false;
381 }
382 static handle from_cpp(MlirTypeID v, rv_policy,
383 cleanup_list *cleanup) noexcept {
384 if (v.ptr == nullptr)
385 return nanobind::none();
386 return mlirFromCppNoThrow([&]() -> handle {
387 nanobind::object capsule =
388 nanobind::steal<nanobind::object>(mlirPythonTypeIDToCapsule(v));
389 return mlir::python::irModule()
390 .attr("TypeID")
391 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
392 .release();
393 });
394 };
395};
396
397/// Casts object <-> MlirType.
398template <>
399struct type_caster<MlirType> {
400 NB_TYPE_CASTER(MlirType, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Type")))
401 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
402 if (auto capsule = mlirApiObjectToCapsule(src)) {
403 value = mlirPythonCapsuleToType(capsule->ptr());
404 return pyErrClearIfFalse(!mlirTypeIsNull(value));
405 }
406 return false;
407 }
408 static handle from_cpp(MlirType t, rv_policy,
409 cleanup_list *cleanup) noexcept {
410 return mlirFromCppNoThrow([&]() -> handle {
411 nanobind::object capsule =
412 nanobind::steal<nanobind::object>(mlirPythonTypeToCapsule(t));
413 return mlir::python::irModule()
414 .attr("Type")
415 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
417 .release();
418 });
419 }
420};
421
422/// Casts MlirStringRef -> object.
423template <>
424struct type_caster<MlirStringRef> {
425 NB_TYPE_CASTER(MlirStringRef, const_name("str"))
426 static handle from_cpp(MlirStringRef s, rv_policy,
427 cleanup_list *cleanup) noexcept {
428 return mlirFromCppNoThrow(
429 [&]() -> handle { return nanobind::str(s.data, s.length).release(); });
430 }
431};
432
433} // namespace detail
434} // namespace nanobind
435
436namespace mlir {
437namespace python {
439
440/// Provides a facility like nanobind::class_ for defining a new class in a
441/// scope, but this allows extension of an arbitrary Python class, defining
442/// methods on it is a similar way. Classes defined in this way are very similar
443/// to if defined in Python in the usual way but use nanobind machinery to
444/// do it. These are not "real" nanobind classes but pure Python classes
445/// with no relation to a concrete C++ class.
446///
447/// Derived from a discussion upstream:
448/// https://github.com/pybind/pybind11/issues/1193
449/// (plus a fair amount of extra curricular poking)
450/// TODO: If this proves useful, see about including it in nanobind.
452public:
453 pure_subclass(nanobind::handle scope, const char *derivedClassName,
454 const nanobind::object &superClass) {
455 nanobind::object pyType =
456 nanobind::borrow<nanobind::object>((PyObject *)&PyType_Type);
457 nanobind::object metaclass = pyType(superClass);
458 nanobind::dict attributes;
459
460 thisClass = metaclass(derivedClassName, nanobind::make_tuple(superClass),
461 attributes);
462 scope.attr(derivedClassName) = thisClass;
463 thisClass.attr("__module__") = scope.attr("__name__");
464 }
465
466 template <typename Func, typename... Extra>
467 pure_subclass &def(const char *name, Func &&f, const Extra &...extra) {
468 nanobind::object cf = nanobind::cpp_function(
469 std::forward<Func>(f), nanobind::name(name), nanobind::is_method(),
470 nanobind::scope(thisClass), extra...);
471 thisClass.attr(name) = cf;
472 return *this;
473 }
474
475 template <typename Func, typename... Extra>
476 pure_subclass &def_property_readonly(const char *name, Func &&f,
477 const Extra &...extra) {
478 nanobind::object cf = nanobind::cpp_function(
479 std::forward<Func>(f), nanobind::name(name), nanobind::is_method(),
480 nanobind::scope(thisClass), extra...);
481 auto builtinProperty =
482 nanobind::borrow<nanobind::object>((PyObject *)&PyProperty_Type);
483 thisClass.attr(name) = builtinProperty(cf);
484 return *this;
485 }
486
487 template <typename Func, typename... Extra>
488 pure_subclass &def_staticmethod(const char *name, Func &&f,
489 const Extra &...extra) {
490 static_assert(!std::is_member_function_pointer<Func>::value,
491 "def_staticmethod(...) called with a non-static member "
492 "function pointer");
493 nanobind::object cf = nanobind::cpp_function(
494 std::forward<Func>(f),
495 nanobind::name(name), // nanobind::scope(thisClass),
496 extra...);
497 thisClass.attr(name) = cf;
498 return *this;
499 }
500
501 template <typename Func, typename... Extra>
502 pure_subclass &def_classmethod(const char *name, Func &&f,
503 const Extra &...extra) {
504 static_assert(!std::is_member_function_pointer<Func>::value,
505 "def_classmethod(...) called with a non-static member "
506 "function pointer");
507 nanobind::object cf = nanobind::cpp_function(
508 std::forward<Func>(f),
509 nanobind::name(name), // nanobind::scope(thisClass),
510 extra...);
511 static SafeInit<nanobind::object> classmethodFn([]() {
512 return std::make_unique<nanobind::object>(
513 nanobind::module_::import_("builtins").attr("classmethod"));
514 });
515 thisClass.attr(name) = classmethodFn.get()(cf);
516 return *this;
517 }
518
519 nanobind::object get_class() const { return thisClass; }
520
521protected:
522 nanobind::object superClass;
523 nanobind::object thisClass;
524};
525
526/// Creates a custom subclass of mlir.ir.Attribute, implementing a casting
527/// constructor and type checking methods.
529public:
530 using IsAFunctionTy = bool (*)(MlirAttribute);
531 using GetTypeIDFunctionTy = MlirTypeID (*)();
532
533 /// Subclasses by looking up the super-class dynamically.
534 mlir_attribute_subclass(nanobind::handle scope, const char *attrClassName,
535 IsAFunctionTy isaFunction,
536 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
537 : mlir_attribute_subclass(scope, attrClassName, isaFunction,
538 irModule().attr("Attribute"),
539 getTypeIDFunction) {}
540
541 /// Subclasses with a provided mlir.ir.Attribute super-class. This must
542 /// be used if the subclass is being defined in the same extension module
543 /// as the mlir.ir class (otherwise, it will trigger a recursive
544 /// initialization).
545 mlir_attribute_subclass(nanobind::handle scope, const char *typeClassName,
546 IsAFunctionTy isaFunction,
547 const nanobind::object &superCls,
548 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
549 : pure_subclass(scope, typeClassName, superCls) {
550 // Casting constructor. Note that it is hard, if not impossible, to properly
551 // call chain to parent `__init__` in nanobind due to its special handling
552 // for init functions that don't have a fully constructed self-reference,
553 // which makes it impossible to forward it to `__init__` of a superclass.
554 // Instead, provide a custom `__new__` and call that of a superclass, which
555 // eventually calls `__init__` of the superclass. Since attribute subclasses
556 // have no additional members, we can just return the instance thus created
557 // without amending it.
558 std::string captureTypeName(
559 typeClassName); // As string in case if typeClassName is not static.
560 nanobind::object newCf = nanobind::cpp_function(
561 [superCls, isaFunction, captureTypeName](
562 nanobind::object cls, nanobind::object otherAttribute) {
563 MlirAttribute rawAttribute;
564 if (!nanobind::try_cast<MlirAttribute>(otherAttribute,
565 rawAttribute) ||
566 !isaFunction(rawAttribute)) {
567 auto origRepr =
568 nanobind::cast<std::string>(nanobind::repr(otherAttribute));
569 throw std::invalid_argument(nanobind::detail::join(
570 "Cannot cast attribute to ", captureTypeName, " (from ",
571 origRepr, ")"));
572 }
573 nanobind::object self = superCls.attr("__new__")(cls, otherAttribute);
574 return self;
575 },
576 nanobind::name("__new__"), nanobind::arg("cls"),
577 nanobind::arg("cast_from_attr"));
578 thisClass.attr("__new__") = newCf;
579
580 // 'isinstance' method.
581 static const char kIsinstanceSig[] =
582 "def isinstance(other_attribute: " MAKE_MLIR_PYTHON_QUALNAME(
583 "ir") ".Attribute) -> bool";
585 "isinstance",
586 [isaFunction](MlirAttribute other) { return isaFunction(other); },
587 nanobind::arg("other_attribute"), nanobind::sig(kIsinstanceSig));
588 def("__repr__", [superCls, captureTypeName](nanobind::object self) {
589 return nanobind::cast<std::string>(
590 nanobind::repr(superCls(self))
591 .attr("replace")(superCls.attr("__name__"), captureTypeName));
592 });
593 if (getTypeIDFunction) {
595 "get_static_typeid",
596 [getTypeIDFunction]() { return getTypeIDFunction(); },
597 // clang-format off
598 nanobind::sig("def get_static_typeid() -> " MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID"))
599 // clang-format on
600 );
601 nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir"))
603 getTypeIDFunction())(nanobind::cpp_function(
604 [thisClass = thisClass](const nanobind::object &mlirAttribute) {
605 return thisClass(mlirAttribute);
606 }));
607 }
608 }
609};
610
611/// Creates a custom subclass of mlir.ir.Type, implementing a casting
612/// constructor and type checking methods.
614public:
615 using IsAFunctionTy = bool (*)(MlirType);
616 using GetTypeIDFunctionTy = MlirTypeID (*)();
617
618 /// Subclasses by looking up the super-class dynamically.
619 mlir_type_subclass(nanobind::handle scope, const char *typeClassName,
620 IsAFunctionTy isaFunction,
621 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
622 : mlir_type_subclass(scope, typeClassName, isaFunction,
623 irModule().attr("Type"), getTypeIDFunction) {}
624
625 /// Subclasses with a provided mlir.ir.Type super-class. This must
626 /// be used if the subclass is being defined in the same extension module
627 /// as the mlir.ir class (otherwise, it will trigger a recursive
628 /// initialization).
629 mlir_type_subclass(nanobind::handle scope, const char *typeClassName,
630 IsAFunctionTy isaFunction,
631 const nanobind::object &superCls,
632 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
633 : pure_subclass(scope, typeClassName, superCls) {
634 // Casting constructor. Note that it is hard, if not impossible, to properly
635 // call chain to parent `__init__` in nanobind due to its special handling
636 // for init functions that don't have a fully constructed self-reference,
637 // which makes it impossible to forward it to `__init__` of a superclass.
638 // Instead, provide a custom `__new__` and call that of a superclass, which
639 // eventually calls `__init__` of the superclass. Since attribute subclasses
640 // have no additional members, we can just return the instance thus created
641 // without amending it.
642 std::string captureTypeName(
643 typeClassName); // As string in case if typeClassName is not static.
644 nanobind::object newCf = nanobind::cpp_function(
645 [superCls, isaFunction, captureTypeName](nanobind::object cls,
646 nanobind::object otherType) {
647 MlirType rawType;
648 if (!nanobind::try_cast<MlirType>(otherType, rawType) ||
649 !isaFunction(rawType)) {
650 auto origRepr =
651 nanobind::cast<std::string>(nanobind::repr(otherType));
652 throw std::invalid_argument(
653 nanobind::detail::join("Cannot cast type to ", captureTypeName,
654 " (from ", origRepr, ")"));
655 }
656 nanobind::object self = superCls.attr("__new__")(cls, otherType);
657 return self;
658 },
659 nanobind::name("__new__"), nanobind::arg("cls"),
660 nanobind::arg("cast_from_type"));
661 thisClass.attr("__new__") = newCf;
662
663 // 'isinstance' method.
664 static const char kIsinstanceSig[] =
665 // clang-format off
666 "def isinstance(other_type: " MAKE_MLIR_PYTHON_QUALNAME("ir.Type") ") -> bool";
667 // clang-format on
669 "isinstance",
670 [isaFunction](MlirType other) { return isaFunction(other); },
671 nanobind::arg("other_type"), nanobind::sig(kIsinstanceSig));
672 def("__repr__", [superCls, captureTypeName](nanobind::object self) {
673 return nanobind::cast<std::string>(
674 nanobind::repr(superCls(self))
675 .attr("replace")(superCls.attr("__name__"), captureTypeName));
676 });
677 if (getTypeIDFunction) {
678 // 'get_static_typeid' method.
679 // This is modeled as a static method instead of a static property because
680 // `def_property_readonly_static` is not available in `pure_subclass` and
681 // we do not want to introduce the complexity that pybind uses to
682 // implement it.
684 "get_static_typeid",
685 [getTypeIDFunction]() { return getTypeIDFunction(); },
686 // clang-format off
687 nanobind::sig("def get_static_typeid() -> " MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID"))
688 // clang-format on
689 );
690 nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir"))
692 getTypeIDFunction())(nanobind::cpp_function(
693 [thisClass = thisClass](const nanobind::object &mlirType) {
694 return thisClass(mlirType);
695 }));
696 }
697 }
698};
699
700/// Creates a custom subclass of mlir.ir.Value, implementing a casting
701/// constructor and type checking methods.
703public:
704 using IsAFunctionTy = bool (*)(MlirValue);
705
706 /// Subclasses by looking up the super-class dynamically.
707 mlir_value_subclass(nanobind::handle scope, const char *valueClassName,
708 IsAFunctionTy isaFunction)
709 : mlir_value_subclass(scope, valueClassName, isaFunction,
710 irModule().attr("Value")) {}
711
712 /// Subclasses with a provided mlir.ir.Value super-class. This must
713 /// be used if the subclass is being defined in the same extension module
714 /// as the mlir.ir class (otherwise, it will trigger a recursive
715 /// initialization).
716 mlir_value_subclass(nanobind::handle scope, const char *valueClassName,
717 IsAFunctionTy isaFunction,
718 const nanobind::object &superCls)
719 : pure_subclass(scope, valueClassName, superCls) {
720 // Casting constructor. Note that it is hard, if not impossible, to properly
721 // call chain to parent `__init__` in nanobind due to its special handling
722 // for init functions that don't have a fully constructed self-reference,
723 // which makes it impossible to forward it to `__init__` of a superclass.
724 // Instead, provide a custom `__new__` and call that of a superclass, which
725 // eventually calls `__init__` of the superclass. Since attribute subclasses
726 // have no additional members, we can just return the instance thus created
727 // without amending it.
728 std::string captureValueName(
729 valueClassName); // As string in case if valueClassName is not static.
730 nanobind::object newCf = nanobind::cpp_function(
731 [superCls, isaFunction, captureValueName](nanobind::object cls,
732 nanobind::object otherValue) {
733 MlirValue rawValue;
734 if (!nanobind::try_cast<MlirValue>(otherValue, rawValue) ||
735 !isaFunction(rawValue)) {
736 auto origRepr =
737 nanobind::cast<std::string>(nanobind::repr(otherValue));
738 throw std::invalid_argument(nanobind::detail::join(
739 "Cannot cast value to ", captureValueName, " (from ", origRepr,
740 ")"));
741 }
742 nanobind::object self = superCls.attr("__new__")(cls, otherValue);
743 return self;
744 },
745 nanobind::name("__new__"), nanobind::arg("cls"),
746 nanobind::arg("cast_from_value"));
747 thisClass.attr("__new__") = newCf;
748
749 // 'isinstance' method.
750 static const char kIsinstanceSig[] =
751 // clang-format off
752 "def isinstance(other_value: " MAKE_MLIR_PYTHON_QUALNAME("ir.Value") ") -> bool";
753 // clang-format on
755 "isinstance",
756 [isaFunction](MlirValue other) { return isaFunction(other); },
757 nanobind::arg("other_value"), nanobind::sig(kIsinstanceSig));
758 }
759};
760
761} // namespace nanobind_adaptors
762
763} // namespace python
764} // namespace mlir
765
766#endif // MLIR_BINDINGS_PYTHON_NANOBINDADAPTORS_H
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
static PyObject * mlirPythonTypeIDToCapsule(MlirTypeID typeID)
Creates a capsule object encapsulating the raw C-API MlirTypeID.
Definition Interop.h:348
#define MLIR_PYTHON_MAYBE_DOWNCAST_ATTR
Attribute on MLIR Python objects that expose a function for downcasting the corresponding Python obje...
Definition Interop.h:118
static MlirBlock mlirPythonCapsuleToBlock(PyObject *capsule)
Extracts an MlirBlock from a capsule as produced from mlirPythonBlockToCapsule.
Definition Interop.h:206
static MlirOperation mlirPythonCapsuleToOperation(PyObject *capsule)
Extracts an MlirOperations from a capsule as produced from mlirPythonOperationToCapsule.
Definition Interop.h:338
static MlirFrozenRewritePatternSet mlirPythonCapsuleToFrozenRewritePatternSet(PyObject *capsule)
Extracts an MlirFrozenRewritePatternSet from a capsule as produced from mlirPythonFrozenRewritePatter...
Definition Interop.h:302
#define MLIR_PYTHON_CAPI_PTR_ATTR
Attribute on MLIR Python objects that expose their C-API pointer.
Definition Interop.h:97
static MlirAttribute mlirPythonCapsuleToAttribute(PyObject *capsule)
Extracts an MlirAttribute from a capsule as produced from mlirPythonAttributeToCapsule.
Definition Interop.h:189
static PyObject * mlirPythonTypeToCapsule(MlirType type)
Creates a capsule object encapsulating the raw C-API MlirType.
Definition Interop.h:367
static MlirAffineMap mlirPythonCapsuleToAffineMap(PyObject *capsule)
Extracts an MlirAffineMap from a capsule as produced from mlirPythonAffineMapToCapsule.
Definition Interop.h:395
static PyObject * mlirPythonOperationToCapsule(MlirOperation operation)
Creates a capsule object encapsulating the raw C-API MlirOperation.
Definition Interop.h:330
static PyObject * mlirPythonAttributeToCapsule(MlirAttribute attribute)
Creates a capsule object encapsulating the raw C-API MlirAttribute.
Definition Interop.h:180
#define MLIR_PYTHON_CAPI_FACTORY_ATTR
Attribute on MLIR Python objects that exposes a factory function for constructing the corresponding P...
Definition Interop.h:110
static MlirModule mlirPythonCapsuleToModule(PyObject *capsule)
Extracts an MlirModule from a capsule as produced from mlirPythonModuleToCapsule.
Definition Interop.h:282
static MlirContext mlirPythonCapsuleToContext(PyObject *capsule)
Extracts a MlirContext from a capsule as produced from mlirPythonContextToCapsule.
Definition Interop.h:224
static MlirTypeID mlirPythonCapsuleToTypeID(PyObject *capsule)
Extracts an MlirTypeID from a capsule as produced from mlirPythonTypeIDToCapsule.
Definition Interop.h:357
static PyObject * mlirPythonAffineMapToCapsule(MlirAffineMap affineMap)
Creates a capsule object encapsulating the raw C-API MlirAffineMap.
Definition Interop.h:386
static PyObject * mlirPythonLocationToCapsule(MlirLocation loc)
Creates a capsule object encapsulating the raw C-API MlirLocation.
Definition Interop.h:255
static MlirDialectRegistry mlirPythonCapsuleToDialectRegistry(PyObject *capsule)
Extracts an MlirDialectRegistry from a capsule as produced from mlirPythonDialectRegistryToCapsule.
Definition Interop.h:245
#define MAKE_MLIR_PYTHON_QUALNAME(local)
Definition Interop.h:57
static MlirType mlirPythonCapsuleToType(PyObject *capsule)
Extracts an MlirType from a capsule as produced from mlirPythonTypeToCapsule.
Definition Interop.h:376
static MlirValue mlirPythonCapsuleToValue(PyObject *capsule)
Extracts an MlirValue from a capsule as produced from mlirPythonValueToCapsule.
Definition Interop.h:454
static PyObject * mlirPythonValueToCapsule(MlirValue value)
Creates a capsule object encapsulating the raw C-API MlirValue.
Definition Interop.h:445
static MlirPassManager mlirPythonCapsuleToPassManager(PyObject *capsule)
Extracts an MlirPassManager from a capsule as produced from mlirPythonPassManagerToCapsule.
Definition Interop.h:320
static PyObject * mlirPythonModuleToCapsule(MlirModule module)
Creates a capsule object encapsulating the raw C-API MlirModule.
Definition Interop.h:273
static PyObject * mlirPythonFrozenRewritePatternSetToCapsule(MlirFrozenRewritePatternSet pm)
Creates a capsule object encapsulating the raw C-API MlirFrozenRewritePatternSet.
Definition Interop.h:293
static MlirLocation mlirPythonCapsuleToLocation(PyObject *capsule)
Extracts an MlirLocation from a capsule as produced from mlirPythonLocationToCapsule.
Definition Interop.h:264
#define MLIR_PYTHON_CAPI_TYPE_CASTER_REGISTER_ATTR
Attribute on main C extension module (_mlir) that corresponds to the type caster registration binding...
Definition Interop.h:130
static PyObject * mlirPythonDialectRegistryToCapsule(MlirDialectRegistry registry)
Creates a capsule object encapsulating the raw C-API MlirDialectRegistry.
Definition Interop.h:235
Safely calls Python initialization code on first use, avoiding deadlocks.
mlir_attribute_subclass(nanobind::handle scope, const char *attrClassName, IsAFunctionTy isaFunction, GetTypeIDFunctionTy getTypeIDFunction=nullptr)
Subclasses by looking up the super-class dynamically.
mlir_attribute_subclass(nanobind::handle scope, const char *typeClassName, IsAFunctionTy isaFunction, const nanobind::object &superCls, GetTypeIDFunctionTy getTypeIDFunction=nullptr)
Subclasses with a provided mlir.ir.Attribute super-class.
mlir_type_subclass(nanobind::handle scope, const char *typeClassName, IsAFunctionTy isaFunction, const nanobind::object &superCls, GetTypeIDFunctionTy getTypeIDFunction=nullptr)
Subclasses with a provided mlir.ir.Type super-class.
mlir_type_subclass(nanobind::handle scope, const char *typeClassName, IsAFunctionTy isaFunction, GetTypeIDFunctionTy getTypeIDFunction=nullptr)
Subclasses by looking up the super-class dynamically.
mlir_value_subclass(nanobind::handle scope, const char *valueClassName, IsAFunctionTy isaFunction, const nanobind::object &superCls)
Subclasses with a provided mlir.ir.Value super-class.
mlir_value_subclass(nanobind::handle scope, const char *valueClassName, IsAFunctionTy isaFunction)
Subclasses by looking up the super-class dynamically.
pure_subclass & def(const char *name, Func &&f, const Extra &...extra)
pure_subclass & def_property_readonly(const char *name, Func &&f, const Extra &...extra)
pure_subclass(nanobind::handle scope, const char *derivedClassName, const nanobind::object &superClass)
pure_subclass & def_classmethod(const char *name, Func &&f, const Extra &...extra)
pure_subclass & def_staticmethod(const char *name, Func &&f, const Extra &...extra)
static bool mlirPassManagerIsNull(MlirPassManager passManager)
Checks if a PassManager is null.
Definition Pass.h:65
static bool mlirAffineMapIsNull(MlirAffineMap affineMap)
Checks whether an affine map is null.
Definition AffineMap.h:47
static bool mlirTypeIsNull(MlirType type)
Checks whether a type is null.
Definition IR.h:1255
static bool mlirContextIsNull(MlirContext context)
Checks whether a context is null.
Definition IR.h:105
static bool mlirBlockIsNull(MlirBlock block)
Checks whether a block is null.
Definition IR.h:1021
static bool mlirLocationIsNull(MlirLocation location)
Checks if the location is null.
Definition IR.h:398
static bool mlirDialectRegistryIsNull(MlirDialectRegistry registry)
Checks if the dialect registry is null.
Definition IR.h:266
static bool mlirTypeIDIsNull(MlirTypeID typeID)
Checks whether a type id is null.
Definition Support.h:201
Include the generated interface declarations.
static handle mlirFromCppNoThrow(F &&f) noexcept
Invokes f, which creates the Python object for a from_cpp conversion below.
bool pyErrClearIfFalse(bool val)
Clears the Python error indicator if the given condition val is false and returns the condition.
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
static std::optional< nanobind::object > mlirApiObjectToCapsule(nanobind::handle apiObject)
Helper to convert a presumed MLIR API object to a capsule, accepting either an explicit Capsule (whic...
A pointer to a sized fragment of a string, not necessarily null-terminated.
Definition Support.h:78
static handle from_cpp(MlirAffineMap v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirAffineMap, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.AffineMap"))) bool from_python(handle src
static handle from_cpp(MlirAttribute v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirAttribute, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Attribute"))) bool from_python(handle src
NB_TYPE_CASTER(MlirBlock, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Block"))) bool from_python(handle src
NB_TYPE_CASTER(MlirContext, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Context"))) bool from_python(handle src
NB_TYPE_CASTER(MlirDialectRegistry, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.DialectRegistry"))) bool from_python(handle src
static handle from_cpp(MlirDialectRegistry v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirFrozenRewritePatternSet, const_name(MAKE_MLIR_PYTHON_QUALNAME("rewrite.FrozenRewritePatternSet"))) bool from_python(handle src
static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy, handle) noexcept
static handle from_cpp(MlirLocation v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirLocation, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Location"))) bool from_python(handle src
NB_TYPE_CASTER(MlirModule, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Module"))) bool from_python(handle src
static handle from_cpp(MlirModule v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirOperation, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Operation"))) bool from_python(handle src
static handle from_cpp(MlirOperation v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirPassManager, const_name(MAKE_MLIR_PYTHON_QUALNAME("passmanager.PassManager"))) bool from_python(handle src
static handle from_cpp(MlirStringRef s, rv_policy, cleanup_list *cleanup) noexcept
static handle from_cpp(MlirTypeID v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirTypeID, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID"))) bool from_python(handle src
NB_TYPE_CASTER(MlirType, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Type"))) bool from_python(handle src
static handle from_cpp(MlirType t, rv_policy, cleanup_list *cleanup) noexcept
uint8_t cleanup_list *cleanup noexcept
NB_TYPE_CASTER(MlirValue, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Value"))) bool from_python(handle src
static handle from_cpp(MlirValue v, rv_policy, cleanup_list *cleanup) noexcept