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