MLIR 24.0.0git
IRCore.h
Go to the documentation of this file.
1//===- IRCore.h - IR helpers of python bindings ---------------------------===//
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
9#ifndef MLIR_BINDINGS_PYTHON_IRCORE_H
10#define MLIR_BINDINGS_PYTHON_IRCORE_H
11
12#include <cstddef>
13#include <exception>
14#include <optional>
15#include <sstream>
16#include <utility>
17#include <vector>
18
19#include "Globals.h"
20#include "NanobindUtils.h"
21#include "mlir-c/AffineExpr.h"
22#include "mlir-c/AffineMap.h"
24#include "mlir-c/Debug.h"
25#include "mlir-c/Diagnostics.h"
27#include "mlir-c/IR.h"
28#include "mlir-c/IntegerSet.h"
29#include "mlir-c/Remarks.h"
30#include "mlir-c/Support.h"
31#include "mlir-c/Transforms.h"
34
35namespace mlir {
36namespace python {
38
39class PyBlock;
40class PyDiagnostic;
43class PyLocation;
45class PyMlirContext;
47class PyModule;
48class PyOperation;
49class PyOperationBase;
50class PyType;
51class PySymbolTable;
52class PyValue;
53
54/// Wrapper for the global LLVM debugging flag.
56 static void set(nanobind::object &o, bool enable);
57 static bool get(const nanobind::object &);
58 static void bind(nanobind::module_ &m);
59
60private:
61 static nanobind::ft_mutex mutex;
62};
63
64/// Template for a reference to a concrete type which captures a python
65/// reference to its underlying python object.
66template <typename T>
68public:
69 PyObjectRef(T *referrent, nanobind::object object)
70 : referrent(referrent), object(std::move(object)) {
71 assert(this->referrent &&
72 "cannot construct PyObjectRef with null referrent");
73 assert(this->object && "cannot construct PyObjectRef with null object");
74 }
75 PyObjectRef(PyObjectRef &&other) noexcept
76 : referrent(other.referrent), object(std::move(other.object)) {
77 other.referrent = nullptr;
78 assert(!other.object);
79 }
81 : referrent(other.referrent), object(other.object /* copies */) {}
83 referrent = other.referrent;
84 object = other.object;
85 return *this;
86 }
87 PyObjectRef &operator=(PyObjectRef &&other) noexcept {
88 referrent = other.referrent;
89 object = std::move(other.object);
90 other.referrent = nullptr;
91 assert(!other.object);
92 return *this;
93 }
94 ~PyObjectRef() = default;
95
97 if (!object)
98 return 0;
99 return Py_REFCNT(object.ptr());
100 }
101
102 /// Releases the object held by this instance, returning it.
103 /// This is the proper thing to return from a function that wants to return
104 /// the reference. Note that this does not work from initializers.
105 nanobind::object releaseObject() {
106 assert(referrent && object);
107 referrent = nullptr;
108 auto stolen = std::move(object);
109 return stolen;
110 }
111
112 T *get() { return referrent; }
114 assert(referrent && object);
115 return referrent;
116 }
117 nanobind::object getObject() {
118 assert(referrent && object);
119 return object;
120 }
121 operator bool() const { return referrent && object; }
122
123 using NBTypedT = nanobind::typed<nanobind::object, T>;
124
125private:
126 T *referrent;
127 nanobind::object object;
128};
129
130/// Tracks an entry in the thread context stack. New entries are pushed onto
131/// here for each with block that activates a new InsertionPoint, Context or
132/// Location.
133///
134/// Pushing either a Location or InsertionPoint also pushes its associated
135/// Context. Pushing a Context will not modify the Location or InsertionPoint
136/// unless if they are from a different context, in which case, they are
137/// cleared.
139public:
145
146 PyThreadContextEntry(FrameKind frameKind, nanobind::object context,
147 nanobind::object insertionPoint,
148 nanobind::object location)
149 : context(std::move(context)), insertionPoint(std::move(insertionPoint)),
150 location(std::move(location)), frameKind(frameKind) {}
151
152 /// Gets the top of stack context and return nullptr if not defined.
154
155 /// Gets the top of stack insertion point and return nullptr if not defined.
156 static PyInsertionPoint *getDefaultInsertionPoint();
157
158 /// Gets the top of stack location and returns nullptr if not defined.
159 static PyLocation *getDefaultLocation();
160
162 PyInsertionPoint *getInsertionPoint();
163 PyLocation *getLocation();
164 FrameKind getFrameKind() { return frameKind; }
165
166 /// Stack management.
167 static PyThreadContextEntry *getTopOfStack();
168 static nanobind::object pushContext(nanobind::object context);
169 static void popContext(PyMlirContext &context);
170 static nanobind::object pushInsertionPoint(nanobind::object insertionPoint);
171 static void popInsertionPoint(PyInsertionPoint &insertionPoint);
172 static nanobind::object pushLocation(nanobind::object location);
173 static void popLocation(PyLocation &location);
174
175 /// Gets the thread local stack.
176 static std::vector<PyThreadContextEntry> &getStack();
177
178private:
179 static void push(FrameKind frameKind, nanobind::object context,
180 nanobind::object insertionPoint, nanobind::object location);
181
182 /// An object reference to the PyContext.
183 nanobind::object context;
184 /// An object reference to the current insertion point.
185 nanobind::object insertionPoint;
186 /// An object reference to the current location.
187 nanobind::object location;
188 // The kind of push that was performed.
189 FrameKind frameKind;
190};
191
192/// Wrapper around MlirLlvmThreadPool
193/// Python object owns the C++ thread pool
195public:
196 PyThreadPool();
198 PyThreadPool(const PyThreadPool &) = delete;
200
201 int getMaxConcurrency() const;
202 MlirLlvmThreadPool get() { return threadPool; }
203
204 std::string _mlir_thread_pool_ptr() const;
205
206private:
207 MlirLlvmThreadPool threadPool;
208};
209
210/// The kinds, emitting policies and file formats of the remark engine
211/// (mlir-c/Remarks.h).
219
220enum class PyRemarkPolicy : std::underlying_type_t<MlirRemarkPolicy> {
223};
224
225enum class PyRemarkFormat : std::underlying_type_t<MlirRemarkFileFormat> {
228};
229
230/// Wrapper around MlirContext.
233public:
234 PyMlirContext() = delete;
235 PyMlirContext(MlirContext context);
236 PyMlirContext(const PyMlirContext &) = delete;
238
239 /// Returns a context reference for the singleton PyMlirContext wrapper for
240 /// the given context.
241 static PyMlirContextRef forContext(MlirContext context);
243
244 /// Accesses the underlying MlirContext.
245 MlirContext get() { return context; }
246
247 /// Gets a strong reference to this context, which will ensure it is kept
248 /// alive for the life of the reference.
249 PyMlirContextRef getRef();
250
251 /// Gets a capsule wrapping the void* within the MlirContext.
252 nanobind::object getCapsule();
253
254 /// Creates a PyMlirContext from the MlirContext wrapped by a capsule.
255 /// Note that PyMlirContext instances are uniqued, so the returned object
256 /// may be a pre-existing object. Ownership of the underlying MlirContext
257 /// is taken by calling this function.
258 static nanobind::object createFromCapsule(nanobind::object capsule);
259
260 /// Gets the count of live context objects. Used for testing.
261 static size_t getLiveCount();
262
263 /// Gets the count of live modules associated with this context.
264 /// Used for testing.
265 size_t getLiveModuleCount();
266
267 /// Enter and exit the context manager.
268 static nanobind::object contextEnter(nanobind::object context);
269 void contextExit(const nanobind::object &excType,
270 const nanobind::object &excVal,
271 const nanobind::object &excTb);
272
273 /// Attaches a Python callback as a diagnostic handler, returning a
274 /// registration object (internally a PyDiagnosticHandler).
275 nanobind::object attachDiagnosticHandler(nanobind::object callback);
276
277 /// Enables the optimization remark engine of the context, streaming to
278 /// `outputFile`, to `callback`, or (without either) to MLIR remark
279 /// diagnostics. Raises ValueError on an invalid combination, when an engine
280 /// is already enabled or when the file cannot be written.
281 void enableRemarks(PyRemarkPolicy policy,
282 const std::optional<std::string> &outputFile,
283 PyRemarkFormat format, const std::string &allFilter,
284 const std::string &passedFilter,
285 const std::string &missedFilter,
286 const std::string &analysisFilter,
287 const std::string &failedFilter, nanobind::object callback,
288 std::optional<bool> printAsEmitRemarks);
289 /// Finalizes and removes the remark engine; no-op when none is enabled.
290 void finalizeRemarks();
291 /// Whether a remark engine is enabled on the context.
292 bool getRemarksEnabled();
293 /// Whether `context` still has a live Python wrapper (false while it is
294 /// being destroyed).
295 static bool isLiveContext(MlirContext context);
296
297 /// Controls whether error diagnostics should be propagated to diagnostic
298 /// handlers, instead of being captured by `ErrorCapture`.
299 void setEmitErrorDiagnostics(bool value) { emitErrorDiagnostics = value; }
300 bool getEmitErrorDiagnostics() { return emitErrorDiagnostics; }
301 struct ErrorCapture;
302
303private:
304 // Interns the mapping of live MlirContext::ptr to PyMlirContext instances,
305 // preserving the relationship that an MlirContext maps to a single
306 // PyMlirContext wrapper. This could be replaced in the future with an
307 // extension mechanism on the MlirContext for stashing user pointers.
308 // Note that this holds a handle, which does not imply ownership.
309 // Mappings will be removed when the context is destructed.
310 using LiveContextMap = std::unordered_map<void *, PyMlirContext *>;
311 static nanobind::ft_mutex live_contexts_mutex;
312 static LiveContextMap &getLiveContexts();
313
314 // Interns all live modules associated with this context. Modules tracked
315 // in this map are valid. When a module is invalidated, it is removed
316 // from this map, and while it still exists as an instance, any
317 // attempt to access it will raise an error.
318 using LiveModuleMap =
319 std::unordered_map<const void *, std::pair<nanobind::handle, PyModule *>>;
320 LiveModuleMap liveModules;
321
322 bool emitErrorDiagnostics = false;
323
324 MlirContext context;
325 friend class PyModule;
326 friend class PyOperation;
327};
328
329/// Used in function arguments when None should resolve to the current context
330/// manager set instance.
332 : public Defaulting<DefaultingPyMlirContext, PyMlirContext> {
333public:
335 static constexpr const char kTypeDescription[] = "_mlir.ir.Context";
336 static PyMlirContext &resolve();
337};
338
339/// Base class for all objects that directly or indirectly depend on an
340/// MlirContext. The lifetime of the context will extend at least to the
341/// lifetime of these instances.
342/// Immutable objects that depend on a context extend this directly.
344public:
345 BaseContextObject(PyMlirContextRef ref) : contextRef(std::move(ref)) {
346 assert(this->contextRef &&
347 "context object constructed with null context ref");
348 }
349
350 /// Accesses the context reference.
351 PyMlirContextRef &getContext() { return contextRef; }
352
353private:
354 PyMlirContextRef contextRef;
355};
356
357/// Wrapper around an MlirLocation.
359public:
360 PyLocation(PyMlirContextRef contextRef, MlirLocation loc)
361 : BaseContextObject(std::move(contextRef)), loc(loc) {}
362
363 operator MlirLocation() const { return loc; }
364 MlirLocation get() const { return loc; }
365
366 /// Enter and exit the context manager.
367 static nanobind::object contextEnter(nanobind::object location);
368 void contextExit(const nanobind::object &excType,
369 const nanobind::object &excVal,
370 const nanobind::object &excTb);
371
372 /// Gets a capsule wrapping the void* within the MlirLocation.
373 nanobind::object getCapsule();
374
375 /// Creates a PyLocation from the MlirLocation wrapped by a capsule.
376 /// Note that PyLocation instances are uniqued, so the returned object
377 /// may be a pre-existing object. Ownership of the underlying MlirLocation
378 /// is taken by calling this function.
379 static PyLocation createFromCapsule(nanobind::object capsule);
380
381 /// Returns the most-derived Location subclass registered for this TypeID,
382 /// or self.
383 nanobind::typed<nanobind::object, PyLocation> maybeDownCast();
384
385private:
386 MlirLocation loc;
387};
388
396
397enum class PyWalkResult : std::underlying_type_t<MlirWalkResult> {
401};
402
403/// Traversal order for operation walk.
404enum class PyWalkOrder : std::underlying_type_t<MlirWalkOrder> {
407};
408
409/// Flags controlling structural operation equivalence and hashing.
410enum class PyOperationEquivalenceFlags : std::underlying_type_t<
411 MlirOperationEquivalenceFlags> {
412 None = MLIR_OPERATION_EQUIVALENCE_NONE,
413 IgnoreLocations = MLIR_OPERATION_EQUIVALENCE_IGNORE_LOCATIONS,
414 IgnoreDiscardableAttrs = MLIR_OPERATION_EQUIVALENCE_IGNORE_DISCARDABLE_ATTRS,
415 IgnoreProperties = MLIR_OPERATION_EQUIVALENCE_IGNORE_PROPERTIES,
416 IgnoreCommutativity = MLIR_OPERATION_EQUIVALENCE_IGNORE_COMMUTATIVITY
417};
418
419/// Python class mirroring the C MlirDiagnostic struct. Note that these structs
420/// are only valid for the duration of a diagnostic callback and attempting
421/// to access them outside of that will raise an exception. This applies to
422/// nested diagnostics (in the notes) as well.
424public:
425 PyDiagnostic(MlirDiagnostic diagnostic) : diagnostic(diagnostic) {}
426 void invalidate();
427 bool isValid() { return valid; }
428 PyDiagnosticSeverity getSeverity();
429 nanobind::typed<nanobind::object, PyLocation> getLocation();
430 nanobind::str getMessage();
431 nanobind::typed<nanobind::tuple, PyDiagnostic> getNotes();
432
433 /// Materialized diagnostic information. This is safe to access outside the
434 /// diagnostic callback.
438 std::string message;
439 std::vector<DiagnosticInfo> notes;
440 };
442
443private:
444 MlirDiagnostic diagnostic;
445
446 void checkValid();
447 /// If notes have been materialized from the diagnostic, then this will
448 /// be populated with the corresponding objects (all castable to
449 /// PyDiagnostic).
450 std::optional<nanobind::tuple> materializedNotes;
451 bool valid = true;
452};
453
454/// Wrapper around an MlirRemark as delivered to a remark callback. Like a
455/// PyDiagnostic it is only valid for the duration of the callback; accessing
456/// an invalidated remark raises ValueError.
458public:
459 explicit PyRemark(MlirRemark remark) : remark(remark) {}
460 void invalidate() { valid = false; }
461 bool isValid() const { return valid; }
462 PyRemarkKind getKind() const;
463 nanobind::str getRemarkName() const;
464 nanobind::str getCategoryName() const;
465 nanobind::str getFullCategoryName() const;
466 nanobind::str getFunctionName() const;
467 nanobind::typed<nanobind::object, PyLocation> getLocation() const;
468 uint64_t getId() const;
469 /// The key/value arguments as a list of `(key, value)` tuples.
470 nanobind::list getArgs() const;
471 /// The textual form of the remark, without its location.
472 nanobind::str getMessage() const;
473
474private:
475 void checkValid() const;
477 bool valid = true;
478};
479
480/// Represents a diagnostic handler attached to the context. The handler's
481/// callback will be invoked with PyDiagnostic instances until the detach()
482/// method is called or the context is destroyed. A diagnostic handler can be
483/// the subject of a `with` block, which will detach it when the block exits.
484///
485/// Since diagnostic handlers can call back into Python code which can do
486/// unsafe things (i.e. recursively emitting diagnostics, raising exceptions,
487/// etc), this is generally not deemed to be a great user-level API. Users
488/// should generally use some form of DiagnosticCollector. If the handler raises
489/// any exceptions, they will just be emitted to stderr and dropped.
490///
491/// The unique usage of this class means that its lifetime management is
492/// different from most other parts of the API. Instances are always created
493/// in an attached state and can transition to a detached state by either:
494/// a) The context being destroyed and unregistering all handlers.
495/// b) An explicit call to detach().
496/// The object may remain live from a Python perspective for an arbitrary time
497/// after detachment, but there is nothing the user can do with it (since there
498/// is no way to attach an existing handler object).
500public:
501 PyDiagnosticHandler(MlirContext context, nanobind::object callback);
503
504 bool isAttached() { return registeredID.has_value(); }
505 bool getHadError() { return hadError; }
506
507 /// Detaches the handler. Does nothing if not attached.
508 void detach();
509
510 nanobind::object contextEnter() { return nanobind::cast(this); }
511 void contextExit(const nanobind::object &excType,
512 const nanobind::object &excVal,
513 const nanobind::object &excTb) {
514 detach();
515 }
516
517private:
518 MlirContext context;
519 nanobind::object callback;
520 std::optional<MlirDiagnosticHandlerID> registeredID;
521 bool hadError = false;
522 friend class PyMlirContext;
523};
524
525/// RAII object that captures any error diagnostics emitted to the provided
526/// context.
529 : ctx(ctx), handlerID(mlirContextAttachDiagnosticHandler(
530 ctx->get(), handler, /*userData=*/this,
531 /*deleteUserData=*/nullptr)) {}
533 mlirContextDetachDiagnosticHandler(ctx->get(), handlerID);
534 assert(errors.empty() && "unhandled captured errors");
535 }
536
537 std::vector<PyDiagnostic::DiagnosticInfo> take() {
538 return std::move(errors);
539 };
540
541private:
543 MlirDiagnosticHandlerID handlerID;
544 std::vector<PyDiagnostic::DiagnosticInfo> errors;
545
546 static MlirLogicalResult handler(MlirDiagnostic diag, void *userData);
547};
548
549/// Wrapper around an MlirDialect. This is exported as `DialectDescriptor` in
550/// order to differentiate it from the `Dialect` base class which is extended by
551/// plugins which extend dialect functionality through extension python code.
552/// This should be seen as the "low-level" object and `Dialect` as the
553/// high-level, user facing object.
555public:
556 PyDialectDescriptor(PyMlirContextRef contextRef, MlirDialect dialect)
557 : BaseContextObject(std::move(contextRef)), dialect(dialect) {}
558
559 MlirDialect get() { return dialect; }
560
561private:
562 MlirDialect dialect;
563};
564
565/// User-level object for accessing dialects with dotted syntax such as:
566/// ctx.dialect.std
568public:
570 : BaseContextObject(std::move(contextRef)) {}
571
572 MlirDialect getDialectForKey(const std::string &key, bool attrError);
573};
574
575/// User-level dialect object. For dialects that have a registered extension,
576/// this will be the base class of the extension dialect type. For un-extended,
577/// objects of this type will be returned directly.
579public:
580 PyDialect(nanobind::object descriptor) : descriptor(std::move(descriptor)) {}
581
582 nanobind::object getDescriptor() { return descriptor; }
583
584private:
585 nanobind::object descriptor;
586};
587
588/// Wrapper around an MlirDialectRegistry.
589/// Upon construction, the Python wrapper takes ownership of the
590/// underlying MlirDialectRegistry.
592public:
594 PyDialectRegistry(MlirDialectRegistry registry) : registry(registry) {}
596 if (!mlirDialectRegistryIsNull(registry))
598 }
601 : registry(other.registry) {
602 other.registry = {nullptr};
603 }
604
605 operator MlirDialectRegistry() const { return registry; }
606 MlirDialectRegistry get() const { return registry; }
607
608 nanobind::object getCapsule();
609 static PyDialectRegistry createFromCapsule(nanobind::object capsule);
610
611private:
612 MlirDialectRegistry registry;
613};
614
615/// Used in function arguments when None should resolve to the current context
616/// manager set instance.
618 : public Defaulting<DefaultingPyLocation, PyLocation> {
619public:
621 static constexpr const char kTypeDescription[] = "_mlir.ir.Location";
622 static PyLocation &resolve();
623
624 operator MlirLocation() const { return *get(); }
625};
626
627/// Wrapper around MlirModule.
628/// This is the top-level, user-owned object that contains regions/ops/blocks.
629class PyModule;
632public:
633 /// Returns a PyModule reference for the given MlirModule. This always returns
634 /// a new object.
635 static PyModuleRef forModule(MlirModule module);
636 PyModule(PyModule &) = delete;
638 ~PyModule();
639
640 /// Gets the backing MlirModule.
641 MlirModule get() { return module; }
642
643 /// Gets a strong reference to this module.
645 return PyModuleRef(this, nanobind::borrow<nanobind::object>(handle));
646 }
647
648 /// Gets a capsule wrapping the void* within the MlirModule.
649 /// Note that the module does not (yet) provide a corresponding factory for
650 /// constructing from a capsule as that would require uniquing PyModule
651 /// instances, which is not currently done.
652 nanobind::object getCapsule();
653
654 /// Creates a PyModule from the MlirModule wrapped by a capsule.
655 /// Note this returns a new object BUT clearMlirModule() must be called to
656 /// prevent double-frees (of the underlying mlir::Module).
657 static nanobind::object createFromCapsule(nanobind::object capsule);
658
659 void clearMlirModule() { module = {nullptr}; }
660
661private:
662 PyModule(PyMlirContextRef contextRef, MlirModule module);
663 MlirModule module;
664 nanobind::handle handle;
665};
666
667class PyAsmState;
668
669/// Base class for PyOperation and PyOpView which exposes the primary, user
670/// visible methods for manipulating it.
672public:
673 virtual ~PyOperationBase() = default;
674 /// Implements the bound 'print' method and helps with others.
675 void print(std::optional<int64_t> largeElementsLimit,
676 std::optional<int64_t> largeResourceLimit, bool enableDebugInfo,
677 bool prettyDebugInfo, bool printGenericOpForm, bool useLocalScope,
678 bool useNameLocAsPrefix, bool assumeVerified,
679 nanobind::object fileObject, bool binary, bool skipRegions);
680 void print(PyAsmState &state, nanobind::object fileObject, bool binary);
681
682 nanobind::object
683 getAsm(bool binary, std::optional<int64_t> largeElementsLimit,
684 std::optional<int64_t> largeResourceLimit, bool enableDebugInfo,
685 bool prettyDebugInfo, bool printGenericOpForm, bool useLocalScope,
686 bool useNameLocAsPrefix, bool assumeVerified, bool skipRegions);
687
688 // Implement the bound 'writeBytecode' method.
689 void writeBytecode(const nanobind::object &fileObject,
690 std::optional<int64_t> bytecodeVersion);
691
692 // Implement the walk method.
693 void walk(std::function<PyWalkResult(MlirOperation)> callback,
694 PyWalkOrder walkOrder);
695
696 /// Moves the operation before or after the other operation.
697 void moveAfter(PyOperationBase &other);
698 void moveBefore(PyOperationBase &other);
699
700 /// Given an operation 'other' that is within the same parent block, return
701 /// whether the current operation is before 'other' in the operation list
702 /// of the parent block.
703 /// Note: This function has an average complexity of O(1), but worst case may
704 /// take O(N) where N is the number of operations within the parent block.
705 bool isBeforeInBlock(PyOperationBase &other);
706
707 /// Verify the operation. Throws `MLIRError` if verification fails, and
708 /// returns `true` otherwise.
709 bool verify();
710
711 /// Each must provide access to the raw Operation.
712 virtual PyOperation &getOperation() = 0;
713};
714
715/// Wrapper around PyOperation.
716/// Operations exist in either an attached (dependent) or detached (top-level)
717/// state. In the detached state (as on creation), an operation is owned by
718/// the creator and its lifetime extends either until its reference count
719/// drops to zero or it is attached to a parent, at which point its lifetime
720/// is bounded by its top-level parent reference.
721class PyOperation;
722class PyOpView;
725 public BaseContextObject {
726public:
727 ~PyOperation() override;
728 PyOperation &getOperation() override { return *this; }
729
730 /// Returns a PyOperation for the given MlirOperation, optionally associating
731 /// it with a parentKeepAlive.
732 static PyOperationRef
733 forOperation(PyMlirContextRef contextRef, MlirOperation operation,
734 nanobind::object parentKeepAlive = nanobind::object());
735
736 /// Creates a detached operation. The operation must not be associated with
737 /// any existing live operation.
738 static PyOperationRef
739 createDetached(PyMlirContextRef contextRef, MlirOperation operation,
740 nanobind::object parentKeepAlive = nanobind::object());
741
742 /// Parses a source string (either text assembly or bytecode), creating a
743 /// detached operation.
744 static PyOperationRef parse(PyMlirContextRef contextRef,
745 const std::string &sourceStr,
746 const std::string &sourceName);
747
748 /// Detaches the operation from its parent block and updates its state
749 /// accordingly.
750 void detachFromParent();
751
752 /// Gets the backing operation.
753 operator MlirOperation() const { return get(); }
754 MlirOperation get() const;
755
756 PyOperationRef getRef();
757
758 bool isAttached() { return attached; }
759 void setAttached(const nanobind::object &parent = nanobind::object());
760 void setDetached();
761 void checkValid() const;
762
763 /// Gets the owning block or raises an exception if the operation has no
764 /// owning block.
765 PyBlock getBlock();
766
767 /// Gets the parent operation or raises an exception if the operation has
768 /// no parent.
769 std::optional<PyOperationRef> getParentOperation();
770
771 /// Gets a capsule wrapping the void* within the MlirOperation.
772 nanobind::object getCapsule();
773
774 /// Creates a PyOperation from the MlirOperation wrapped by a capsule.
775 /// Ownership of the underlying MlirOperation is taken by calling this
776 /// function.
777 static nanobind::object createFromCapsule(const nanobind::object &capsule);
778
779 /// Creates an operation. See corresponding python docstring.
780 static nanobind::object
781 create(std::string_view name, std::optional<std::vector<PyType *>> results,
782 const MlirValue *operands, size_t numOperands,
783 std::optional<nanobind::dict> attributes,
784 std::optional<std::vector<PyBlock *>> successors, int regions,
785 PyLocation &location, const nanobind::object &ip, bool inferType);
786
787 /// Creates an OpView suitable for this operation.
788 nanobind::object createOpView();
789
790 /// Erases the underlying MlirOperation, removes its pointer from the
791 /// parent context's live operations map, and sets the valid bit false.
792 void erase();
793
794 /// Invalidate the operation.
795 void setInvalid() { valid = false; }
796
797 /// Clones this operation.
798 nanobind::object clone(const nanobind::object &ip);
799
800 PyOperation(PyMlirContextRef contextRef, MlirOperation operation);
801
802private:
803 static PyOperationRef createInstance(PyMlirContextRef contextRef,
804 MlirOperation operation,
805 nanobind::object parentKeepAlive);
806
807 MlirOperation operation;
808 nanobind::handle handle;
809 // Keeps the parent alive, regardless of whether it is an Operation or
810 // Module.
811 // TODO: As implemented, this facility is only sufficient for modeling the
812 // trivial module parent back-reference. Generalize this to also account for
813 // transitions from detached to attached and address TODOs in the
814 // ir_operation.py regarding testing corresponding lifetime guarantees.
815 nanobind::object parentKeepAlive;
816 bool attached = true;
817 bool valid = true;
818
819 friend class PyOperationBase;
820 friend class PySymbolTable;
821};
822
823/// A PyOpView is equivalent to the C++ "Op" wrappers: these are the basis for
824/// providing more instance-specific accessors and serve as the base class for
825/// custom ODS-style operation classes. Since this class is subclass on the
826/// python side, it must present an __init__ method that operates in pure
827/// python types.
829public:
830 PyOpView(const nanobind::object &operationObject);
831 PyOperation &getOperation() override { return operation; }
832
833 nanobind::object getOperationObject() { return operationObject; }
834
835 static nanobind::typed<nanobind::object, PyOperation>
836 buildGeneric(std::string_view name, std::tuple<int, bool> opRegionSpec,
837 nanobind::object operandSegmentSpecObj,
838 nanobind::object resultSegmentSpecObj,
839 std::optional<nanobind::sequence> resultTypeList,
840 nanobind::sequence operandList,
841 std::optional<nanobind::dict> attributes,
842 std::optional<std::vector<PyBlock *>> successors,
843 std::optional<int> regions, PyLocation &location,
844 const nanobind::object &maybeIp);
845
846 /// Construct an instance of a class deriving from OpView, bypassing its
847 /// `__init__` method. The derived class will typically define a constructor
848 /// that provides a convenient builder, but we need to side-step this when
849 /// constructing an `OpView` for an already-built operation.
850 ///
851 /// The caller is responsible for verifying that `operation` is a valid
852 /// operation to construct `cls` with.
853 static nanobind::object constructDerived(const nanobind::object &cls,
854 const nanobind::object &operation);
855
856private:
857 PyOperation &operation; // For efficient, cast-free access from C++
858 nanobind::object operationObject; // Holds the reference.
859};
860
861/// Wrapper around an MlirRegion.
862/// Regions are managed completely by their containing operation. Unlike the
863/// C++ API, the python API does not support detached regions.
865public:
866 PyRegion(PyOperationRef parentOperation, MlirRegion region)
867 : parentOperation(std::move(parentOperation)), region(region) {
868 assert(!mlirRegionIsNull(region) && "python region cannot be null");
869 }
870 operator MlirRegion() const { return region; }
871
872 MlirRegion get() { return region; }
873 PyOperationRef &getParentOperation() { return parentOperation; }
874
875 void checkValid() { return parentOperation->checkValid(); }
876
877private:
878 PyOperationRef parentOperation;
879 MlirRegion region;
880};
881
882/// Wrapper around an MlirAsmState.
884public:
885 PyAsmState(MlirValue value, bool useLocalScope);
886 PyAsmState(PyOperationBase &operation, bool useLocalScope);
888 // Delete copy constructors.
889 PyAsmState(PyAsmState &other) = delete;
890 PyAsmState(const PyAsmState &other) = delete;
891
892 MlirAsmState get() { return state; }
893
894private:
895 MlirAsmState state;
896 MlirOpPrintingFlags flags;
897};
898
899/// Wrapper around an MlirBlock.
900/// Blocks are managed completely by their containing operation. Unlike the
901/// C++ API, the python API does not support detached blocks.
903public:
904 PyBlock(PyOperationRef parentOperation, MlirBlock block)
905 : parentOperation(std::move(parentOperation)), block(block) {
906 assert(!mlirBlockIsNull(block) && "python block cannot be null");
907 }
908
909 MlirBlock get() { return block; }
910 PyOperationRef &getParentOperation() { return parentOperation; }
911
912 void checkValid() { return parentOperation->checkValid(); }
913
914 /// Gets a capsule wrapping the void* within the MlirBlock.
915 nanobind::object getCapsule();
916
917private:
918 PyOperationRef parentOperation;
919 MlirBlock block;
920};
921
922/// An insertion point maintains a pointer to a Block and a reference operation.
923/// Calls to insert() will insert a new operation before the
924/// reference operation. If the reference operation is null, then appends to
925/// the end of the block.
927public:
928 /// Creates an insertion point positioned after the last operation in the
929 /// block, but still inside the block.
930 PyInsertionPoint(const PyBlock &block);
931 /// Creates an insertion point positioned before a reference operation.
932 PyInsertionPoint(PyOperationBase &beforeOperationBase);
933 /// Creates an insertion point positioned before a reference operation.
934 PyInsertionPoint(PyOperationRef beforeOperationRef);
935
936 /// Shortcut to create an insertion point at the beginning of the block.
938 /// Shortcut to create an insertion point before the block terminator.
940 /// Shortcut to create an insertion point to the node after the specified
941 /// operation.
943
944 /// Inserts an operation.
945 void insert(PyOperationBase &operationBase);
946
947 /// Enter and exit the context manager.
948 static nanobind::object contextEnter(nanobind::object insertionPoint);
949 void contextExit(const nanobind::object &excType,
950 const nanobind::object &excVal,
951 const nanobind::object &excTb);
952
953 PyBlock &getBlock() { return block; }
954 std::optional<PyOperationRef> &getRefOperation() { return refOperation; }
955
956private:
957 // Trampoline constructor that avoids null initializing members while
958 // looking up parents.
959 PyInsertionPoint(PyBlock block, std::optional<PyOperationRef> refOperation)
960 : refOperation(std::move(refOperation)), block(std::move(block)) {}
961
962 std::optional<PyOperationRef> refOperation;
963 PyBlock block;
964};
965
966/// Wrapper around the generic MlirType.
967/// The lifetime of a type is bound by the PyContext that created it.
969public:
970 PyType(PyMlirContextRef contextRef, MlirType type)
971 : BaseContextObject(std::move(contextRef)), type(type) {}
972 bool operator==(const PyType &other) const;
973 operator MlirType() const { return type; }
974 MlirType get() const { return type; }
975
976 /// Gets a capsule wrapping the void* within the MlirType.
977 nanobind::object getCapsule();
978
979 /// Creates a PyType from the MlirType wrapped by a capsule.
980 /// Note that PyType instances are uniqued, so the returned object
981 /// may be a pre-existing object. Ownership of the underlying MlirType
982 /// is taken by calling this function.
983 static PyType createFromCapsule(nanobind::object capsule);
984
985 nanobind::typed<nanobind::object, PyType> maybeDownCast();
986
987private:
988 MlirType type;
989};
990
991/// A TypeID provides an efficient and unique identifier for a specific C++
992/// type. This allows for a C++ type to be compared, hashed, and stored in an
993/// opaque context. This class wraps around the generic MlirTypeID.
995public:
996 PyTypeID(MlirTypeID typeID) : typeID(typeID) {}
997 // Note, this tests whether the underlying TypeIDs are the same,
998 // not whether the wrapper MlirTypeIDs are the same, nor whether
999 // the PyTypeID objects are the same (i.e., PyTypeID is a value type).
1000 bool operator==(const PyTypeID &other) const;
1001 operator MlirTypeID() const { return typeID; }
1002 MlirTypeID get() { return typeID; }
1003
1004 /// Gets a capsule wrapping the void* within the MlirTypeID.
1005 nanobind::object getCapsule();
1006
1007 /// Creates a PyTypeID from the MlirTypeID wrapped by a capsule.
1008 static PyTypeID createFromCapsule(nanobind::object capsule);
1009
1010private:
1011 MlirTypeID typeID;
1012};
1013
1014/// CRTP base classes for Python types that subclass Type and should be
1015/// castable from it (i.e. via something like IntegerType(t)).
1016/// By default, type class hierarchies are one level deep (i.e. a
1017/// concrete type class extends PyType); however, intermediate python-visible
1018/// base classes can be modeled by specifying a BaseTy.
1019template <typename DerivedTy, typename BaseTy = PyType>
1021public:
1022 // Derived classes must define statics for:
1023 // IsAFunctionTy isaFunction
1024 // const char *pyClassName
1025 using ClassTy = nanobind::class_<DerivedTy, BaseTy>;
1026 using IsAFunctionTy = bool (*)(MlirType);
1027 using GetTypeIDFunctionTy = MlirTypeID (*)();
1029 static constexpr GetTypeIDFunctionTy getTypeIdFunction = nullptr;
1030 static inline const MlirStringRef name{};
1031
1032 PyConcreteType() = default;
1033 PyConcreteType(PyMlirContextRef contextRef, MlirType t)
1034 : BaseTy(std::move(contextRef), t) {}
1036 : PyConcreteType(orig.getContext(), castFrom(orig)) {}
1037
1038 static MlirType castFrom(PyType &orig) {
1039 if (!DerivedTy::isaFunction(orig)) {
1040 auto origRepr =
1041 nanobind::cast<std::string>(nanobind::repr(nanobind::cast(orig)));
1042 throw nanobind::value_error((std::string("Cannot cast type to ") +
1043 DerivedTy::pyClassName + " (from " +
1044 origRepr + ")")
1045 .c_str());
1046 }
1047 return orig;
1048 }
1049
1050 static void bind(nanobind::module_ &m) {
1051 auto cls = ClassTy(m, DerivedTy::pyClassName, nanobind::is_generic());
1052 cls.def(nanobind::init<PyType &>(), nanobind::keep_alive<0, 1>(),
1053 nanobind::arg("cast_from_type"));
1054 cls.def_prop_ro_static("static_typeid", [](nanobind::object & /*class*/) {
1055 if (DerivedTy::getTypeIdFunction)
1056 return PyTypeID(DerivedTy::getTypeIdFunction());
1057 throw nanobind::attribute_error(
1058 (DerivedTy::pyClassName + std::string(" has no typeid.")).c_str());
1059 });
1060 cls.def_prop_ro("typeid", [](PyType &self) {
1061 return nanobind::cast<PyTypeID>(nanobind::cast(self).attr("typeid"));
1062 });
1063 cls.def("__repr__", [](DerivedTy &self) {
1064 PyPrintAccumulator printAccum;
1065 printAccum.parts.append(DerivedTy::pyClassName);
1066 printAccum.parts.append("(");
1067 mlirTypePrint(self, printAccum.getCallback(), printAccum.getUserData());
1068 printAccum.parts.append(")");
1069 return printAccum.join();
1070 });
1071
1072 if (DerivedTy::getTypeIdFunction) {
1073 PyGlobals::get().registerTypeCaster(
1074 DerivedTy::getTypeIdFunction(),
1075 nanobind::cast<nanobind::callable>(nanobind::cpp_function(
1076 [](PyType pyType) -> DerivedTy { return DerivedTy(pyType); })),
1077 /*replace*/ true);
1078 }
1079
1080 if (DerivedTy::name.length != 0) {
1081 cls.def_prop_ro_static("type_name", [](nanobind::object & /*self*/) {
1082 return nanobind::str(DerivedTy::name.data, DerivedTy::name.length);
1083 });
1084 }
1085
1086 DerivedTy::bindDerived(cls);
1087 }
1088
1089 /// Implemented by derived classes to add methods to the Python subclass.
1090 static void bindDerived(ClassTy &m) {}
1091};
1092
1093/// Wrapper around the generic MlirAttribute.
1094/// The lifetime of a type is bound by the PyContext that created it.
1096public:
1097 PyAttribute(PyMlirContextRef contextRef, MlirAttribute attr)
1098 : BaseContextObject(std::move(contextRef)), attr(attr) {}
1099 bool operator==(const PyAttribute &other) const;
1100 operator MlirAttribute() const { return attr; }
1101 MlirAttribute get() const { return attr; }
1102
1103 /// Gets a capsule wrapping the void* within the MlirAttribute.
1104 nanobind::object getCapsule();
1105
1106 /// Creates a PyAttribute from the MlirAttribute wrapped by a capsule.
1107 /// Note that PyAttribute instances are uniqued, so the returned object
1108 /// may be a pre-existing object. Ownership of the underlying MlirAttribute
1109 /// is taken by calling this function.
1110 static PyAttribute createFromCapsule(const nanobind::object &capsule);
1111
1112 nanobind::typed<nanobind::object, PyAttribute> maybeDownCast();
1113
1114private:
1115 MlirAttribute attr;
1116};
1117
1118/// Represents a Python MlirNamedAttr, carrying an optional owned name.
1119/// TODO: Refactor this and the C-API to be based on an Identifier owned
1120/// by the context so as to avoid ownership issues here.
1122public:
1123 /// Constructs a PyNamedAttr that retains an owned name. This should be
1124 /// used in any code that originates an MlirNamedAttribute from a python
1125 /// string.
1126 /// The lifetime of the PyNamedAttr must extend to the lifetime of the
1127 /// passed attribute.
1128 PyNamedAttribute(MlirAttribute attr, std::string ownedName);
1129
1131
1132private:
1133 // Since the MlirNamedAttr contains an internal pointer to the actual
1134 // memory of the owned string, it must be heap allocated to remain valid.
1135 // Otherwise, strings that fit within the small object optimization threshold
1136 // will have their memory address change as the containing object is moved,
1137 // resulting in an invalid aliased pointer.
1138 std::unique_ptr<std::string> ownedName;
1139};
1140
1141/// CRTP base classes for Python attributes that subclass Attribute and should
1142/// be castable from it (i.e. via something like StringAttr(attr)).
1143/// By default, attribute class hierarchies are one level deep (i.e. a
1144/// concrete attribute class extends PyAttribute); however, intermediate
1145/// python-visible base classes can be modeled by specifying a BaseTy.
1146template <typename DerivedTy, typename BaseTy = PyAttribute>
1148public:
1149 // Derived classes must define statics for:
1150 // IsAFunctionTy isaFunction
1151 // const char *pyClassName
1152 using ClassTy = nanobind::class_<DerivedTy, BaseTy>;
1153 using IsAFunctionTy = bool (*)(MlirAttribute);
1154 using GetTypeIDFunctionTy = MlirTypeID (*)();
1155 static constexpr GetTypeIDFunctionTy getTypeIdFunction = nullptr;
1156 static inline const MlirStringRef name{};
1158
1160 PyConcreteAttribute(PyMlirContextRef contextRef, MlirAttribute attr)
1161 : BaseTy(std::move(contextRef), attr) {}
1164
1165 static MlirAttribute castFrom(PyAttribute &orig) {
1166 if (!DerivedTy::isaFunction(orig)) {
1167 auto origRepr =
1168 nanobind::cast<std::string>(nanobind::repr(nanobind::cast(orig)));
1169 throw nanobind::value_error((std::string("Cannot cast attribute to ") +
1170 DerivedTy::pyClassName + " (from " +
1171 origRepr + ")")
1172 .c_str());
1173 }
1174 return orig;
1175 }
1176
1177 static void bind(nanobind::module_ &m, PyType_Slot *slots = nullptr) {
1178 ClassTy cls;
1179 if (slots) {
1180 cls = ClassTy(m, DerivedTy::pyClassName, nanobind::type_slots(slots),
1181 nanobind::is_generic());
1182 } else {
1183 cls = ClassTy(m, DerivedTy::pyClassName, nanobind::is_generic());
1184 }
1185 cls.def(nanobind::init<PyAttribute &>(), nanobind::keep_alive<0, 1>(),
1186 nanobind::arg("cast_from_attr"));
1187 cls.def_prop_ro(
1188 "type",
1189 [](PyAttribute &attr) -> nanobind::typed<nanobind::object, PyType> {
1190 return PyType(attr.getContext(), mlirAttributeGetType(attr))
1191 .maybeDownCast();
1192 });
1193 cls.def_prop_ro_static("static_typeid", [](nanobind::object & /*class*/) {
1194 if (DerivedTy::getTypeIdFunction)
1195 return PyTypeID(DerivedTy::getTypeIdFunction());
1196 throw nanobind::attribute_error(
1197 (DerivedTy::pyClassName + std::string(" has no typeid.")).c_str());
1198 });
1199 cls.def_prop_ro("typeid", [](PyAttribute &self) {
1200 return nanobind::cast<PyTypeID>(nanobind::cast(self).attr("typeid"));
1201 });
1202 cls.def("__repr__", [](DerivedTy &self) {
1203 PyPrintAccumulator printAccum;
1204 printAccum.parts.append(DerivedTy::pyClassName);
1205 printAccum.parts.append("(");
1206 mlirAttributePrint(self, printAccum.getCallback(),
1207 printAccum.getUserData());
1208 printAccum.parts.append(")");
1209 return printAccum.join();
1210 });
1211
1212 if (DerivedTy::getTypeIdFunction) {
1213 PyGlobals::get().registerTypeCaster(
1214 DerivedTy::getTypeIdFunction(),
1215 nanobind::cast<nanobind::callable>(
1216 nanobind::cpp_function([](PyAttribute pyAttribute) -> DerivedTy {
1217 return DerivedTy(pyAttribute);
1218 })),
1219 /*replace*/ true);
1220 }
1221
1222 if (DerivedTy::name.length != 0) {
1223 cls.def_prop_ro_static("attr_name", [](nanobind::object & /*self*/) {
1224 return nanobind::str(DerivedTy::name.data, DerivedTy::name.length);
1225 });
1226 }
1227
1228 DerivedTy::bindDerived(cls);
1229 }
1230
1231 /// Implemented by derived classes to add methods to the Python subclass.
1232 static void bindDerived(ClassTy &m) {}
1233};
1234
1236 : public PyConcreteAttribute<PyStringAttribute> {
1237public:
1239 static constexpr const char *pyClassName = "StringAttr";
1244
1245 static void bindDerived(ClassTy &c);
1246};
1247
1248/// CRTP base class for Python classes that subclass Location and should be
1249/// castable from it (i.e. via something like FileLineColLoc(loc)).
1250template <typename DerivedTy, typename BaseTy = PyLocation>
1252public:
1253 // Derived classes must define statics for:
1254 // IsAFunctionTy isaFunction
1255 // const char *pyClassName
1256 using ClassTy = nanobind::class_<DerivedTy, BaseTy>;
1257 using IsAFunctionTy = bool (*)(MlirLocation);
1258 using GetTypeIDFunctionTy = MlirTypeID (*)();
1259 static constexpr GetTypeIDFunctionTy getTypeIdFunction = nullptr;
1261
1263 PyConcreteLocation(PyMlirContextRef contextRef, MlirLocation loc)
1264 : BaseTy(std::move(contextRef), loc) {}
1267
1268 static MlirLocation castFrom(PyLocation &orig) {
1269 if (!DerivedTy::isaFunction(orig.get())) {
1270 auto origRepr =
1271 nanobind::cast<std::string>(nanobind::repr(nanobind::cast(orig)));
1272 throw nanobind::value_error((std::string("Cannot cast location to ") +
1273 DerivedTy::pyClassName + " (from " +
1274 origRepr + ")")
1275 .c_str());
1276 }
1277 return orig.get();
1278 }
1279
1280 static void bind(nanobind::module_ &m) {
1281 ClassTy cls(m, DerivedTy::pyClassName, nanobind::is_generic());
1282 cls.def(nanobind::init<PyLocation &>(), nanobind::keep_alive<0, 1>(),
1283 nanobind::arg("cast_from_loc"));
1284 cls.def_prop_ro_static("static_typeid", [](nanobind::object & /*class*/) {
1285 if (DerivedTy::getTypeIdFunction)
1286 return PyTypeID(DerivedTy::getTypeIdFunction());
1287 throw nanobind::attribute_error(
1288 (DerivedTy::pyClassName + std::string(" has no typeid.")).c_str());
1289 });
1290 cls.def("__repr__", [](DerivedTy &self) {
1291 PyPrintAccumulator printAccum;
1292 printAccum.parts.append(DerivedTy::pyClassName);
1293 printAccum.parts.append("(");
1294 mlirLocationPrint(self, printAccum.getCallback(),
1295 printAccum.getUserData());
1296 printAccum.parts.append(")");
1297 return printAccum.join();
1298 });
1299 if (DerivedTy::getTypeIdFunction) {
1300 PyGlobals::get().registerTypeCaster(
1301 DerivedTy::getTypeIdFunction(),
1302 nanobind::cast<nanobind::callable>(nanobind::cpp_function(
1303 [](PyLocation pyLoc) -> DerivedTy { return DerivedTy(pyLoc); })),
1304 /*replace*/ true);
1305 }
1306 DerivedTy::bindDerived(cls);
1307 }
1308
1309 /// Implemented by derived classes to add methods to the Python subclass.
1310 static void bindDerived(ClassTy &m) {}
1311};
1312
1314 : public PyConcreteLocation<PyUnknownLocation> {
1315public:
1317 static constexpr const char *pyClassName = "UnknownLoc";
1321
1322 static void bindDerived(ClassTy &c);
1323};
1324
1326 : public PyConcreteLocation<PyFileLineColLocation> {
1327public:
1329 static constexpr const char *pyClassName = "FileLineColLoc";
1333
1334 static void bindDerived(ClassTy &c);
1335};
1336
1338 : public PyConcreteLocation<PyNameLocation> {
1339public:
1341 static constexpr const char *pyClassName = "NameLoc";
1345
1346 static void bindDerived(ClassTy &c);
1347};
1348
1350 : public PyConcreteLocation<PyCallSiteLocation> {
1351public:
1353 static constexpr const char *pyClassName = "CallSiteLoc";
1357
1358 static void bindDerived(ClassTy &c);
1359};
1360
1362 : public PyConcreteLocation<PyFusedLocation> {
1363public:
1365 static constexpr const char *pyClassName = "FusedLoc";
1369
1370 static void bindDerived(ClassTy &c);
1371};
1372
1373/// Wrapper around the generic MlirValue.
1374/// Values are managed completely by the operation that resulted in their
1375/// definition. For op result value, this is the operation that defines the
1376/// value. For block argument values, this is the operation that contains the
1377/// block to which the value is an argument (blocks cannot be detached in Python
1378/// bindings so such operation always exists).
1379class PyBlockArgument;
1380class PyOpResult;
1382public:
1383 // The virtual here is "load bearing" in that it enables RTTI
1384 // for PyConcreteValue CRTP classes that support maybeDownCast.
1385 // See PyValue::maybeDownCast.
1386 virtual ~PyValue() = default;
1387 PyValue(PyOperationRef parentOperation, MlirValue value)
1388 : parentOperation(std::move(parentOperation)), value(value) {}
1389 operator MlirValue() const { return value; }
1390
1391 MlirValue get() { return value; }
1392 PyOperationRef &getParentOperation() { return parentOperation; }
1393
1394 void checkValid() { return parentOperation->checkValid(); }
1395
1396 /// Gets a capsule wrapping the void* within the MlirValue.
1397 nanobind::object getCapsule();
1398
1399 nanobind::typed<nanobind::object,
1400 std::variant<PyBlockArgument, PyOpResult, PyValue>>
1401 maybeDownCast();
1402
1403 /// Creates a PyValue from the MlirValue wrapped by a capsule. Ownership of
1404 /// the underlying MlirValue is still tied to the owning operation.
1405 static PyValue createFromCapsule(nanobind::object capsule);
1406
1407private:
1408 PyOperationRef parentOperation;
1409 MlirValue value;
1410};
1411
1412/// Wrapper around MlirAffineExpr. Affine expressions are owned by the context.
1414public:
1415 PyAffineExpr(PyMlirContextRef contextRef, MlirAffineExpr affineExpr)
1416 : BaseContextObject(std::move(contextRef)), affineExpr(affineExpr) {}
1417 bool operator==(const PyAffineExpr &other) const;
1418 operator MlirAffineExpr() const { return affineExpr; }
1419 MlirAffineExpr get() const { return affineExpr; }
1420
1421 /// Gets a capsule wrapping the void* within the MlirAffineExpr.
1422 nanobind::object getCapsule();
1423
1424 /// Creates a PyAffineExpr from the MlirAffineExpr wrapped by a capsule.
1425 /// Note that PyAffineExpr instances are uniqued, so the returned object
1426 /// may be a pre-existing object. Ownership of the underlying MlirAffineExpr
1427 /// is taken by calling this function.
1428 static PyAffineExpr createFromCapsule(const nanobind::object &capsule);
1429
1430 PyAffineExpr add(const PyAffineExpr &other) const;
1431 PyAffineExpr mul(const PyAffineExpr &other) const;
1433 PyAffineExpr ceilDiv(const PyAffineExpr &other) const;
1434 PyAffineExpr mod(const PyAffineExpr &other) const;
1435
1436 nanobind::typed<nanobind::object, PyAffineExpr> maybeDownCast();
1437
1438private:
1439 MlirAffineExpr affineExpr;
1440};
1441
1443public:
1444 PyAffineMap(PyMlirContextRef contextRef, MlirAffineMap affineMap)
1445 : BaseContextObject(std::move(contextRef)), affineMap(affineMap) {}
1446 bool operator==(const PyAffineMap &other) const;
1447 operator MlirAffineMap() const { return affineMap; }
1448 MlirAffineMap get() const { return affineMap; }
1449
1450 /// Gets a capsule wrapping the void* within the MlirAffineMap.
1451 nanobind::object getCapsule();
1452
1453 /// Creates a PyAffineMap from the MlirAffineMap wrapped by a capsule.
1454 /// Note that PyAffineMap instances are uniqued, so the returned object
1455 /// may be a pre-existing object. Ownership of the underlying MlirAffineMap
1456 /// is taken by calling this function.
1457 static PyAffineMap createFromCapsule(const nanobind::object &capsule);
1458
1459private:
1460 MlirAffineMap affineMap;
1461};
1462
1464public:
1465 PyIntegerSet(PyMlirContextRef contextRef, MlirIntegerSet integerSet)
1466 : BaseContextObject(std::move(contextRef)), integerSet(integerSet) {}
1467 bool operator==(const PyIntegerSet &other) const;
1468 operator MlirIntegerSet() const { return integerSet; }
1469 MlirIntegerSet get() const { return integerSet; }
1470
1471 /// Gets a capsule wrapping the void* within the MlirIntegerSet.
1472 nanobind::object getCapsule();
1473
1474 /// Creates a PyIntegerSet from the MlirAffineMap wrapped by a capsule.
1475 /// Note that PyIntegerSet instances may be uniqued, so the returned object
1476 /// may be a pre-existing object. Integer sets are owned by the context.
1477 static PyIntegerSet createFromCapsule(const nanobind::object &capsule);
1478
1479private:
1480 MlirIntegerSet integerSet;
1481};
1482
1483/// Bindings for MLIR symbol tables.
1485public:
1486 /// Constructs a symbol table for the given operation.
1487 explicit PySymbolTable(PyOperationBase &operation);
1488
1489 /// Destroys the symbol table.
1491
1492 /// Returns the symbol (opview) with the given name, throws if there is no
1493 /// such symbol in the table.
1494 nanobind::object dunderGetItem(const std::string &name);
1495
1496 /// Removes the given operation from the symbol table and erases it.
1497 void erase(PyOperationBase &symbol);
1498
1499 /// Removes the operation with the given name from the symbol table and erases
1500 /// it, throws if there is no such symbol in the table.
1501 void dunderDel(const std::string &name);
1502
1503 /// Inserts the given operation into the symbol table. The operation must have
1504 /// the symbol trait.
1505 PyStringAttribute insert(PyOperationBase &symbol);
1506
1507 /// Gets and sets the name of a symbol op.
1508 static PyStringAttribute getSymbolName(PyOperationBase &symbol);
1509 static void setSymbolName(PyOperationBase &symbol, const std::string &name);
1510
1511 /// Gets and sets the visibility of a symbol op.
1512 static PyStringAttribute getVisibility(PyOperationBase &symbol);
1513 static void setVisibility(PyOperationBase &symbol,
1514 const std::string &visibility);
1515
1516 /// Replaces all symbol uses within an operation. See the API
1517 /// mlirSymbolTableReplaceAllSymbolUses for all caveats.
1518 static void replaceAllSymbolUses(const std::string &oldSymbol,
1519 const std::string &newSymbol,
1520 PyOperationBase &from);
1521
1522 /// Walks all symbol tables under and including 'from'.
1523 static void walkSymbolTables(PyOperationBase &from, bool allSymUsesVisible,
1524 nanobind::object callback);
1525
1526 /// Casts the bindings class into the C API structure.
1527 operator MlirSymbolTable() { return symbolTable; }
1528
1529private:
1530 PyOperationRef operation;
1531 MlirSymbolTable symbolTable;
1532};
1533
1534/// Custom exception that allows access to error diagnostic information. This is
1535/// translated to the `ir.MLIRError` python exception when thrown.
1536struct MLIR_PYTHON_API_EXPORTED MLIRError : std::exception {
1537 MLIRError(std::string message,
1538 std::vector<PyDiagnostic::DiagnosticInfo> &&errorDiagnostics = {})
1539 : message(std::move(message)),
1540 errorDiagnostics(std::move(errorDiagnostics)) {}
1541 const char *what() const noexcept override { return message.c_str(); }
1542
1543 /// Bind the MLIRError exception class to the given module.
1544 static void bind(nanobind::module_ &m);
1545
1546 std::string message;
1547 std::vector<PyDiagnostic::DiagnosticInfo> errorDiagnostics;
1548};
1549
1550//------------------------------------------------------------------------------
1551// Utilities.
1552//------------------------------------------------------------------------------
1553
1554inline MlirStringRef toMlirStringRef(const std::string &s) {
1555 return mlirStringRefCreate(s.data(), s.size());
1556}
1557
1558inline MlirStringRef toMlirStringRef(std::string_view s) {
1559 return mlirStringRefCreate(s.data(), s.size());
1560}
1561
1562inline MlirStringRef toMlirStringRef(const nanobind::bytes &s) {
1563 return mlirStringRefCreate(static_cast<const char *>(s.data()), s.size());
1564}
1565
1566/// Create a block, using the current location context if no locations are
1567/// specified.
1569createBlock(const nanobind::typed<nanobind::sequence, PyType> &pyArgTypes,
1570 const std::optional<nanobind::typed<nanobind::sequence, PyLocation>>
1571 &pyArgLocs);
1572
1574 static bool dunderContains(const std::string &attributeKind);
1575 static nanobind::callable
1576 dunderGetItemNamed(const std::string &attributeKind);
1577 static void dunderSetItemNamed(const std::string &attributeKind,
1578 nanobind::callable func, bool replace,
1579 bool allow_existing);
1580
1581 static void bind(nanobind::module_ &m);
1582};
1583
1584//------------------------------------------------------------------------------
1585// Collections.
1586//------------------------------------------------------------------------------
1587
1588/// Regions of an op are fixed length and indexed numerically so are represented
1589/// with a sequence-like container.
1591 : public Sliceable<PyRegionList, PyRegion> {
1592public:
1593 static constexpr const char *pyClassName = "RegionSequence";
1594
1596 intptr_t length = -1, intptr_t step = 1);
1597
1598private:
1599 /// Give the parent CRTP class access to hook implementations below.
1600 friend class Sliceable<PyRegionList, PyRegion>;
1601
1602 intptr_t getRawNumElements();
1603
1604 PyRegion getRawElement(intptr_t pos);
1605
1607
1608 PyOperationRef operation;
1609};
1610
1612public:
1613 PyBlockIterator(PyOperationRef operation, MlirBlock next)
1614 : operation(std::move(operation)), next(next) {}
1615
1616 PyBlockIterator &dunderIter() { return *this; }
1617
1618 nanobind::typed<nanobind::object, PyBlock> dunderNext();
1619
1620 static void bind(nanobind::module_ &m);
1621
1622private:
1623 PyOperationRef operation;
1624 MlirBlock next;
1625};
1626
1627/// Blocks are exposed by the C-API as a forward-only linked list. In Python,
1628/// we present them as a more full-featured list-like container but optimize
1629/// it for forward iteration. Blocks are always owned by a region.
1631public:
1632 PyBlockList(PyOperationRef operation, MlirRegion region)
1633 : operation(std::move(operation)), region(region) {}
1634
1635 PyBlockIterator dunderIter();
1636
1637 intptr_t dunderLen();
1638
1639 PyBlock dunderGetItem(intptr_t index);
1640
1641 PyBlock appendBlock(const nanobind::args &pyArgTypes,
1642 const std::optional<nanobind::sequence> &pyArgLocs);
1643
1644 static void bind(nanobind::module_ &m);
1645
1646private:
1647 PyOperationRef operation;
1648 MlirRegion region;
1649};
1650
1652public:
1653 PyOperationIterator(PyOperationRef parentOperation, MlirOperation next)
1654 : parentOperation(std::move(parentOperation)), next(next) {}
1655
1656 PyOperationIterator &dunderIter() { return *this; }
1657
1658 nanobind::typed<nanobind::object, PyOpView> dunderNext();
1659
1660 static void bind(nanobind::module_ &m);
1661
1662private:
1663 PyOperationRef parentOperation;
1664 MlirOperation next;
1665};
1666
1667/// Operations are exposed by the C-API as a forward-only linked list. In
1668/// Python, we present them as a more full-featured list-like container but
1669/// optimize it for forward iteration. Iterable operations are always owned
1670/// by a block.
1672public:
1673 PyOperationList(PyOperationRef parentOperation, MlirBlock block)
1674 : parentOperation(std::move(parentOperation)), block(block) {}
1675
1676 PyOperationIterator dunderIter();
1677
1678 intptr_t dunderLen();
1679
1680 nanobind::typed<nanobind::object, PyOpView> dunderGetItem(intptr_t index);
1681
1682 static void bind(nanobind::module_ &m);
1683
1684private:
1685 PyOperationRef parentOperation;
1686 MlirBlock block;
1687};
1688
1690public:
1691 PyOpOperand(MlirOpOperand opOperand) : opOperand(opOperand) {}
1692 operator MlirOpOperand() const { return opOperand; }
1693
1694 nanobind::typed<nanobind::object, PyOpView> getOwner() const;
1695
1696 size_t getOperandNumber() const;
1697
1698 static void bind(nanobind::module_ &m);
1699
1700private:
1701 MlirOpOperand opOperand;
1702};
1703
1705public:
1706 PyOpOperandIterator(MlirOpOperand opOperand) : opOperand(opOperand) {}
1707
1708 PyOpOperandIterator &dunderIter() { return *this; }
1709
1710 nanobind::typed<nanobind::object, PyOpOperand> dunderNext();
1711
1712 static void bind(nanobind::module_ &m);
1713
1714private:
1715 MlirOpOperand opOperand;
1716};
1717
1718/// CRTP base class for Python MLIR values that subclass Value and should be
1719/// castable from it. The value hierarchy is one level deep and is not supposed
1720/// to accommodate other levels unless core MLIR changes.
1721template <typename DerivedTy>
1723public:
1724 // Derived classes must define statics for:
1725 // IsAFunctionTy isaFunction
1726 // const char *pyClassName
1727 // and redefine bindDerived.
1728 using ClassTy = nanobind::class_<DerivedTy, PyValue>;
1729 using IsAFunctionTy = bool (*)(MlirValue);
1730 using GetTypeIDFunctionTy = MlirTypeID (*)();
1731 static constexpr GetTypeIDFunctionTy getTypeIdFunction = nullptr;
1733
1734 PyConcreteValue() = default;
1735 PyConcreteValue(PyOperationRef operationRef, MlirValue value)
1736 : PyValue(operationRef, value) {}
1739
1740 /// Attempts to cast the original value to the derived type and throws on
1741 /// type mismatches.
1742 static MlirValue castFrom(PyValue &orig) {
1743 if (!DerivedTy::isaFunction(orig.get())) {
1744 auto origRepr =
1745 nanobind::cast<std::string>(nanobind::repr(nanobind::cast(orig)));
1746 throw nanobind::value_error((std::string("Cannot cast value to ") +
1747 DerivedTy::pyClassName + " (from " +
1748 origRepr + ")")
1749 .c_str());
1750 }
1751 return orig.get();
1752 }
1753
1754 /// Binds the Python module objects to functions of this class.
1755 static void bind(nanobind::module_ &m) {
1756 auto cls = ClassTy(m, DerivedTy::pyClassName, nanobind::is_generic(),
1757 nanobind::sig((std::string("class ") +
1758 DerivedTy::pyClassName + "(Value[_T])")
1759 .c_str()));
1760 cls.def(nanobind::init<PyValue &>(), nanobind::keep_alive<0, 1>(),
1761 nanobind::arg("value"));
1762 cls.def(
1764 [](DerivedTy &self) -> nanobind::typed<nanobind::object, DerivedTy> {
1765 return self.maybeDownCast();
1766 });
1767 cls.def("__str__", [](PyValue &self) {
1768 PyPrintAccumulator printAccum;
1769 printAccum.parts.append(std::string(DerivedTy::pyClassName) + "(");
1770 mlirValuePrint(self.get(), printAccum.getCallback(),
1771 printAccum.getUserData());
1772 printAccum.parts.append(")");
1773 return printAccum.join();
1774 });
1775
1776 if (DerivedTy::getTypeIdFunction) {
1777 PyGlobals::get().registerValueCaster(
1778 DerivedTy::getTypeIdFunction(),
1779 nanobind::cast<nanobind::callable>(nanobind::cpp_function(
1780 [](PyValue pyValue) -> DerivedTy { return DerivedTy(pyValue); })),
1781 /*replace*/ true);
1782 }
1783
1784 DerivedTy::bindDerived(cls);
1785 }
1786
1787 /// Implemented by derived classes to add methods to the Python subclass.
1788 static void bindDerived(ClassTy &m) {}
1789};
1790
1791/// Python wrapper for MlirOpResult.
1793public:
1795 static constexpr const char *pyClassName = "OpResult";
1797
1798 static void bindDerived(ClassTy &c);
1799};
1800
1801/// A list of operation results. Internally, these are stored as consecutive
1802/// elements, random access is cheap. The (returned) result list is associated
1803/// with the operation whose results these are, and thus extends the lifetime of
1804/// this operation.
1806 : public Sliceable<PyOpResultList, PyOpResult> {
1807public:
1808 static constexpr const char *pyClassName = "OpResultList";
1809 static constexpr std::array<const char *, 1> typeParams = {"_T"};
1811
1813 intptr_t length = -1, intptr_t step = 1);
1814
1815 static void bindDerived(ClassTy &c);
1816
1817 PyOperationRef &getOperation() { return operation; }
1818
1819private:
1820 /// Give the parent CRTP class access to hook implementations below.
1821 friend class Sliceable<PyOpResultList, PyOpResult>;
1822
1823 intptr_t getRawNumElements();
1824
1825 PyOpResult getRawElement(intptr_t index);
1826
1827 PyOpResultList slice(intptr_t startIndex, intptr_t length,
1828 intptr_t step) const;
1829
1830 PyOperationRef operation;
1831};
1832
1833/// Python wrapper for MlirBlockArgument.
1835 : public PyConcreteValue<PyBlockArgument> {
1836public:
1838 static constexpr const char *pyClassName = "BlockArgument";
1840
1841 static void bindDerived(ClassTy &c);
1842};
1843
1844/// A list of block arguments. Internally, these are stored as consecutive
1845/// elements, random access is cheap. The argument list is associated with the
1846/// operation that contains the block (detached blocks are not allowed in
1847/// Python bindings) and extends its lifetime.
1849 : public Sliceable<PyBlockArgumentList, PyBlockArgument> {
1850public:
1851 static constexpr const char *pyClassName = "BlockArgumentList";
1853
1854 PyBlockArgumentList(PyOperationRef operation, MlirBlock block,
1856 intptr_t step = 1);
1857
1858 static void bindDerived(ClassTy &c);
1859
1860private:
1861 /// Give the parent CRTP class access to hook implementations below.
1863
1864 /// Returns the number of arguments in the list.
1865 intptr_t getRawNumElements();
1866
1867 /// Returns `pos`-the element in the list.
1868 PyBlockArgument getRawElement(intptr_t pos) const;
1869
1870 /// Returns a sublist of this list.
1872 intptr_t step) const;
1873
1874 PyOperationRef operation;
1875 MlirBlock block;
1876};
1877
1878/// A list of operation operands. Internally, these are stored as consecutive
1879/// elements, random access is cheap. The (returned) operand list is associated
1880/// with the operation whose operands these are, and thus extends the lifetime
1881/// of this operation.
1883 : public Sliceable<PyOpOperandList, PyValue> {
1884public:
1885 static constexpr const char *pyClassName = "OpOperandList";
1886 static constexpr std::array<const char *, 1> typeParams = {"_T"};
1888
1890 intptr_t length = -1, intptr_t step = 1);
1891
1892 void dunderSetItem(intptr_t index, PyValue value);
1893
1894 static void bindDerived(ClassTy &c);
1895
1896private:
1897 /// Give the parent CRTP class access to hook implementations below.
1898 friend class Sliceable<PyOpOperandList, PyValue>;
1899
1900 intptr_t getRawNumElements();
1901
1902 PyValue getRawElement(intptr_t pos);
1903
1905 intptr_t step) const;
1906
1907 PyOperationRef operation;
1908};
1909
1910/// A list of operation successors. Internally, these are stored as consecutive
1911/// elements, random access is cheap. The (returned) successor list is
1912/// associated with the operation whose successors these are, and thus extends
1913/// the lifetime of this operation.
1915 : public Sliceable<PyOpSuccessors, PyBlock> {
1916public:
1917 static constexpr const char *pyClassName = "OpSuccessors";
1918
1920 intptr_t length = -1, intptr_t step = 1);
1921
1922 void dunderSetItem(intptr_t index, PyBlock block);
1923
1924 static void bindDerived(ClassTy &c);
1925
1926private:
1927 /// Give the parent CRTP class access to hook implementations below.
1928 friend class Sliceable<PyOpSuccessors, PyBlock>;
1929
1930 intptr_t getRawNumElements();
1931
1932 PyBlock getRawElement(intptr_t pos);
1933
1935 intptr_t step) const;
1936
1937 PyOperationRef operation;
1938};
1939
1940/// A list of block successors. Internally, these are stored as consecutive
1941/// elements, random access is cheap. The (returned) successor list is
1942/// associated with the operation and block whose successors these are, and thus
1943/// extends the lifetime of this operation and block.
1945 : public Sliceable<PyBlockSuccessors, PyBlock> {
1946public:
1947 static constexpr const char *pyClassName = "BlockSuccessors";
1948
1951 intptr_t step = 1);
1952
1953private:
1954 /// Give the parent CRTP class access to hook implementations below.
1955 friend class Sliceable<PyBlockSuccessors, PyBlock>;
1956
1957 intptr_t getRawNumElements();
1958
1959 PyBlock getRawElement(intptr_t pos);
1960
1962 intptr_t step) const;
1963
1964 PyOperationRef operation;
1965 PyBlock block;
1966};
1967
1968/// A list of block predecessors. The (returned) predecessor list is
1969/// associated with the operation and block whose predecessors these are, and
1970/// thus extends the lifetime of this operation and block.
1971///
1972/// WARNING: This Sliceable is more expensive than the others here because
1973/// mlirBlockGetPredecessor actually iterates the use-def chain (of block
1974/// operands) anew for each indexed access.
1976 : public Sliceable<PyBlockPredecessors, PyBlock> {
1977public:
1978 static constexpr const char *pyClassName = "BlockPredecessors";
1979
1982 intptr_t step = 1);
1983
1984private:
1985 /// Give the parent CRTP class access to hook implementations below.
1986 friend class Sliceable<PyBlockPredecessors, PyBlock>;
1987
1988 intptr_t getRawNumElements();
1989
1990 PyBlock getRawElement(intptr_t pos);
1991
1993 intptr_t step) const;
1994
1995 PyOperationRef operation;
1996 PyBlock block;
1997};
1998
1999/// A list of operation attributes. Can be indexed by name, producing
2000/// attributes, or by index, producing named attributes.
2002public:
2004 : operation(std::move(operation)) {}
2005
2006 nanobind::typed<nanobind::object, PyAttribute>
2007 dunderGetItemNamed(const std::string &name);
2008
2009 PyNamedAttribute dunderGetItemIndexed(intptr_t index);
2010
2011 nanobind::typed<nanobind::object, std::optional<PyAttribute>>
2012 get(const std::string &key, nanobind::object defaultValue);
2013
2014 void dunderSetItem(const std::string &name, const PyAttribute &attr);
2015
2016 void dunderDelItem(const std::string &name);
2017
2018 intptr_t dunderLen();
2019
2020 bool dunderContains(const std::string &name);
2021
2022 static void forEachAttr(MlirOperation op,
2023 std::function<void(MlirStringRef, MlirAttribute)> fn);
2024
2025 static void bind(nanobind::module_ &m);
2026
2027private:
2028 PyOperationRef operation;
2029};
2030
2031/// Base class of operation adaptors.
2033public:
2034 PyOpAdaptor(nanobind::list operands, PyOpAttributeMap attributes)
2035 : operands(std::move(operands)), attributes(std::move(attributes)) {}
2036 PyOpAdaptor(nanobind::list operands, PyOpView &opView)
2037 : operands(std::move(operands)),
2038 attributes(opView.getOperation().getRef()) {}
2039
2040 static void bind(nanobind::module_ &m);
2041
2042private:
2043 nanobind::list operands;
2044 PyOpAttributeMap attributes;
2045};
2046
2048public:
2049 static bool attach(const nanobind::object &opName,
2050 const nanobind::object &target, PyMlirContext &context);
2051
2052 static void bind(nanobind::module_ &m);
2053
2054 static inline const char *typeIDAttr = "_trait_typeid";
2055};
2056
2058
2060public:
2061 static bool attach(const nanobind::object &opName, PyMlirContext &context);
2062 static void bind(nanobind::module_ &m);
2063};
2064
2066public:
2067 static bool attach(const nanobind::object &opName, PyMlirContext &context);
2068 static void bind(nanobind::module_ &m);
2069};
2070
2072public:
2073 static bool attach(const nanobind::object &opName, PyMlirContext &context);
2074 static void bind(nanobind::module_ &m);
2075};
2076
2078 : public PyDynamicOpTrait {
2079public:
2080 static bool attach(const nanobind::object &opName, PyMlirContext &context);
2081 static void bind(nanobind::module_ &m);
2082};
2083
2084} // namespace PyDynamicOpTraits
2085
2086MLIR_PYTHON_API_EXPORTED MlirValue getUniqueResult(MlirOperation operation);
2087MLIR_PYTHON_API_EXPORTED void populateIRCore(nanobind::module_ &m);
2089MLIR_PYTHON_API_EXPORTED void populateRoot(nanobind::module_ &m);
2090
2091/// Helper for creating an @classmethod.
2092template <class Func, typename... Args>
2093inline nanobind::object classmethod(Func f, Args... args) {
2094 nanobind::object cf = nanobind::cpp_function(f, args...);
2095 static SafeInit<nanobind::object> classmethodFn([]() {
2096 return std::make_unique<nanobind::object>(
2097 nanobind::module_::import_("builtins").attr("classmethod"));
2098 });
2099 return classmethodFn.get()(cf);
2100}
2101
2102} // namespace MLIR_BINDINGS_PYTHON_DOMAIN
2103} // namespace python
2104} // namespace mlir
2105
2106namespace nanobind {
2107namespace detail {
2108template <>
2109struct type_caster<
2110 mlir::python::MLIR_BINDINGS_PYTHON_DOMAIN::DefaultingPyMlirContext>
2112 mlir::python::MLIR_BINDINGS_PYTHON_DOMAIN::DefaultingPyMlirContext> {
2113};
2114template <>
2115struct type_caster<
2116 mlir::python::MLIR_BINDINGS_PYTHON_DOMAIN::DefaultingPyLocation>
2118 mlir::python::MLIR_BINDINGS_PYTHON_DOMAIN::DefaultingPyLocation> {};
2119
2120} // namespace detail
2121} // namespace nanobind
2122
2123#endif // MLIR_BINDINGS_PYTHON_IRCORE_H
MLIR_FLOAT16_EXPORT bool operator==(const f16 &f1, const f16 &f2)
bool mlirValueIsABlockArgument(MlirValue value)
Definition IR.cpp:1178
void mlirLocationPrint(MlirLocation location, MlirStringCallback callback, void *userData)
Definition IR.cpp:440
MlirType mlirAttributeGetType(MlirAttribute attribute)
Definition IR.cpp:1357
bool mlirValueIsAOpResult(MlirValue value)
Definition IR.cpp:1182
void mlirOpPrintingFlagsDestroy(MlirOpPrintingFlags flags)
Definition IR.cpp:223
void mlirTypePrint(MlirType type, MlirStringCallback callback, void *userData)
Definition IR.cpp:1338
#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
b getContext())
static void print(spirv::VerCapExtAttr triple, DialectAsmPrinter &printer)
static sycl::context getDefaultContext()
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
A CRTP base class for pseudo-containers willing to support Python-type slicing access on top of index...
nanobind::class_< PyOpResultList > ClassTy
Sliceable(intptr_t startIndex, intptr_t length, intptr_t step)
Defaulting()=default
Type casters require the type to be default constructible, but using such an instance is illegal.
PyMlirContextRef & getContext()
Accesses the context reference.
Definition IRCore.h:351
Used in function arguments when None should resolve to the current context manager set instance.
Definition IRCore.h:618
Defaulting()=default
Type casters require the type to be default constructible, but using such an instance is illegal.
Used in function arguments when None should resolve to the current context manager set instance.
Definition IRCore.h:332
Defaulting()=default
Type casters require the type to be default constructible, but using such an instance is illegal.
Wrapper around MlirAffineExpr. Affine expressions are owned by the context.
Definition IRCore.h:1413
PyAffineExpr ceilDiv(const PyAffineExpr &other) const
PyAffineExpr floorDiv(const PyAffineExpr &other) const
PyAffineExpr add(const PyAffineExpr &other) const
PyAffineExpr mod(const PyAffineExpr &other) const
PyAffineExpr(PyMlirContextRef contextRef, MlirAffineExpr affineExpr)
Definition IRCore.h:1415
nanobind::typed< nanobind::object, PyAffineExpr > maybeDownCast()
Definition IRAffine.cpp:369
PyAffineExpr mul(const PyAffineExpr &other) const
PyAffineMap(PyMlirContextRef contextRef, MlirAffineMap affineMap)
Definition IRCore.h:1444
PyAsmState(MlirValue value, bool useLocalScope)
Definition IRCore.cpp:1748
Wrapper around the generic MlirAttribute.
Definition IRCore.h:1095
PyAttribute(PyMlirContextRef contextRef, MlirAttribute attr)
Definition IRCore.h:1097
Sliceable< PyBlockArgumentList, PyBlockArgument > SliceableT
Definition IRCore.h:1852
PyBlockArgumentList(PyOperationRef operation, MlirBlock block, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2204
Python wrapper for MlirBlockArgument.
Definition IRCore.h:1835
PyBlockIterator(PyOperationRef operation, MlirBlock next)
Definition IRCore.h:1613
PyBlockList(PyOperationRef operation, MlirRegion region)
Definition IRCore.h:1632
PyBlockPredecessors(PyBlock block, PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2364
PyBlockSuccessors(PyBlock block, PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2341
PyBlock(PyOperationRef parentOperation, MlirBlock block)
Definition IRCore.h:904
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1355
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1155
static void bindDerived(ClassTy &m)
Implemented by derived classes to add methods to the Python subclass.
Definition IRCore.h:1232
PyConcreteAttribute(PyMlirContextRef contextRef, MlirAttribute attr)
Definition IRCore.h:1160
static void bind(nanobind::module_ &m, PyType_Slot *slots=nullptr)
Definition IRCore.h:1177
nanobind::class_< DerivedTy, BaseTy > ClassTy
Definition IRCore.h:1152
static MlirAttribute castFrom(PyAttribute &orig)
Definition IRCore.h:1165
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1259
static void bindDerived(ClassTy &m)
Implemented by derived classes to add methods to the Python subclass.
Definition IRCore.h:1310
static MlirLocation castFrom(PyLocation &orig)
Definition IRCore.h:1268
PyConcreteLocation(PyMlirContextRef contextRef, MlirLocation loc)
Definition IRCore.h:1263
nanobind::class_< DerivedTy, BaseTy > ClassTy
Definition IRCore.h:1256
nanobind::class_< DerivedTy, BaseTy > ClassTy
Definition IRCore.h:1025
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1029
PyConcreteType(PyMlirContextRef contextRef, MlirType t)
Definition IRCore.h:1033
static void bindDerived(ClassTy &m)
Implemented by derived classes to add methods to the Python subclass.
Definition IRCore.h:1090
PyConcreteValue(PyOperationRef operationRef, MlirValue value)
Definition IRCore.h:1735
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1731
static void bind(nanobind::module_ &m)
Binds the Python module objects to functions of this class.
Definition IRCore.h:1755
nanobind::class_< DerivedTy, PyValue > ClassTy
Definition IRCore.h:1728
static MlirValue castFrom(PyValue &orig)
Attempts to cast the original value to the derived type and throws on type mismatches.
Definition IRCore.h:1742
static void bindDerived(ClassTy &m)
Implemented by derived classes to add methods to the Python subclass.
Definition IRCore.h:1788
Represents a diagnostic handler attached to the context.
Definition IRCore.h:499
void detach()
Detaches the handler. Does nothing if not attached.
Definition IRCore.cpp:728
PyDiagnosticHandler(MlirContext context, nanobind::object callback)
Definition IRCore.cpp:722
void contextExit(const nanobind::object &excType, const nanobind::object &excVal, const nanobind::object &excTb)
Definition IRCore.h:511
Python class mirroring the C MlirDiagnostic struct.
Definition IRCore.h:423
PyDialectDescriptor(PyMlirContextRef contextRef, MlirDialect dialect)
Definition IRCore.h:556
Wrapper around an MlirDialectRegistry.
Definition IRCore.h:591
PyDialectRegistry(PyDialectRegistry &&other) noexcept
Definition IRCore.h:600
static bool attach(const nanobind::object &opName, const nanobind::object &target, PyMlirContext &context)
Definition IRCore.cpp:2569
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2669
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2623
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2646
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2694
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1331
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1367
static PyGlobals & get()
Most code should get the globals via this static accessor.
Definition Globals.cpp:59
An insertion point maintains a pointer to a Block and a reference operation.
Definition IRCore.h:926
void insert(PyOperationBase &operationBase)
Inserts an operation.
Definition IRCore.cpp:1779
void contextExit(const nanobind::object &excType, const nanobind::object &excVal, const nanobind::object &excTb)
Definition IRCore.cpp:1844
static PyInsertionPoint atBlockTerminator(PyBlock &block)
Shortcut to create an insertion point before the block terminator.
Definition IRCore.cpp:1818
static PyInsertionPoint after(PyOperationBase &op)
Shortcut to create an insertion point to the node after the specified operation.
Definition IRCore.cpp:1827
std::optional< PyOperationRef > & getRefOperation()
Definition IRCore.h:954
static PyInsertionPoint atBlockBegin(PyBlock &block)
Shortcut to create an insertion point at the beginning of the block.
Definition IRCore.cpp:1805
PyInsertionPoint(const PyBlock &block)
Creates an insertion point positioned after the last operation in the block, but still inside the blo...
Definition IRCore.cpp:1770
static nanobind::object contextEnter(nanobind::object insertionPoint)
Enter and exit the context manager.
Definition IRCore.cpp:1840
PyIntegerSet(PyMlirContextRef contextRef, MlirIntegerSet integerSet)
Definition IRCore.h:1465
PyLocation(PyMlirContextRef contextRef, MlirLocation loc)
Definition IRCore.h:360
static PyMlirContextRef forContext(MlirContext context)
Returns a context reference for the singleton PyMlirContext wrapper for the given context.
Definition IRCore.cpp:461
MlirContext get()
Accesses the underlying MlirContext.
Definition IRCore.h:245
void setEmitErrorDiagnostics(bool value)
Controls whether error diagnostics should be propagated to diagnostic handlers, instead of being capt...
Definition IRCore.h:299
PyModuleRef getRef()
Gets a strong reference to this module.
Definition IRCore.h:644
MlirModule get()
Gets the backing MlirModule.
Definition IRCore.h:641
static PyModuleRef forModule(MlirModule module)
Returns a PyModule reference for the given MlirModule.
Definition IRCore.cpp:873
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1343
Represents a Python MlirNamedAttr, carrying an optional owned name.
Definition IRCore.h:1121
PyNamedAttribute(MlirAttribute attr, std::string ownedName)
Constructs a PyNamedAttr that retains an owned name.
Definition IRCore.cpp:1905
Template for a reference to a concrete type which captures a python reference to its underlying pytho...
Definition IRCore.h:67
nanobind::typed< nanobind::object, T > NBTypedT
Definition IRCore.h:123
PyObjectRef & operator=(const PyObjectRef &other)
Definition IRCore.h:82
PyObjectRef(PyObjectRef &&other) noexcept
Definition IRCore.h:75
nanobind::object releaseObject()
Releases the object held by this instance, returning it.
Definition IRCore.h:105
PyObjectRef(T *referrent, nanobind::object object)
Definition IRCore.h:69
PyObjectRef & operator=(PyObjectRef &&other) noexcept
Definition IRCore.h:87
PyOpAdaptor(nanobind::list operands, PyOpAttributeMap attributes)
Definition IRCore.h:2034
PyOpAdaptor(nanobind::list operands, PyOpView &opView)
Definition IRCore.h:2036
Sliceable< PyOpOperandList, PyValue > SliceableT
Definition IRCore.h:1887
static constexpr std::array< const char *, 1 > typeParams
Definition IRCore.h:1886
PyOpOperandList(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2236
void dunderSetItem(intptr_t index, PyValue value)
Definition IRCore.cpp:2244
Sliceable< PyOpResultList, PyOpResult > SliceableT
Definition IRCore.h:1810
static constexpr std::array< const char *, 1 > typeParams
Definition IRCore.h:1809
PyOpResultList(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:1398
static constexpr IsAFunctionTy isaFunction
Definition IRCore.h:1794
PyOpSuccessors(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2308
void dunderSetItem(intptr_t index, PyBlock block)
Definition IRCore.cpp:2316
A PyOpView is equivalent to the C++ "Op" wrappers: these are the basis for providing more instance-sp...
Definition IRCore.h:828
PyOpView(const nanobind::object &operationObject)
Definition IRCore.cpp:1738
PyOperation & getOperation() override
Each must provide access to the raw Operation.
Definition IRCore.h:831
Base class for PyOperation and PyOpView which exposes the primary, user visible methods for manipulat...
Definition IRCore.h:671
bool isBeforeInBlock(PyOperationBase &other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Definition IRCore.cpp:1162
nanobind::object getAsm(bool binary, std::optional< int64_t > largeElementsLimit, std::optional< int64_t > largeResourceLimit, bool enableDebugInfo, bool prettyDebugInfo, bool printGenericOpForm, bool useLocalScope, bool useNameLocAsPrefix, bool assumeVerified, bool skipRegions)
Definition IRCore.cpp:1116
void writeBytecode(const nanobind::object &fileObject, std::optional< int64_t > bytecodeVersion)
Definition IRCore.cpp:1063
virtual PyOperation & getOperation()=0
Each must provide access to the raw Operation.
void moveAfter(PyOperationBase &other)
Moves the operation before or after the other operation.
Definition IRCore.cpp:1144
void walk(std::function< PyWalkResult(MlirOperation)> callback, PyWalkOrder walkOrder)
Definition IRCore.cpp:1084
PyOperationIterator(PyOperationRef parentOperation, MlirOperation next)
Definition IRCore.h:1653
PyOperationList(PyOperationRef parentOperation, MlirBlock block)
Definition IRCore.h:1673
void setInvalid()
Invalidate the operation.
Definition IRCore.h:795
PyOperation & getOperation() override
Each must provide access to the raw Operation.
Definition IRCore.h:728
PyOperation(PyMlirContextRef contextRef, MlirOperation operation)
Definition IRCore.cpp:913
PyRegionList(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:194
PyRegion(PyOperationRef parentOperation, MlirRegion region)
Definition IRCore.h:866
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1241
PySymbolTable(PyOperationBase &operation)
Constructs a symbol table for the given operation.
Definition IRCore.cpp:2021
Tracks an entry in the thread context stack.
Definition IRCore.h:138
PyThreadContextEntry(FrameKind frameKind, nanobind::object context, nanobind::object insertionPoint, nanobind::object location)
Definition IRCore.h:146
A TypeID provides an efficient and unique identifier for a specific C++ type.
Definition IRCore.h:994
Wrapper around the generic MlirType.
Definition IRCore.h:968
PyType(PyMlirContextRef contextRef, MlirType type)
Definition IRCore.h:970
nanobind::typed< nanobind::object, PyType > maybeDownCast()
Definition IRCore.cpp:1933
static constexpr GetTypeIDFunctionTy getTypeIdFunction
Definition IRCore.h:1319
PyValue(PyOperationRef parentOperation, MlirValue value)
Definition IRCore.h:1387
Safely calls Python initialization code on first use, avoiding deadlocks.
MlirDiagnosticSeverity
Severity of a diagnostic.
Definition Diagnostics.h:32
@ MlirDiagnosticNote
Definition Diagnostics.h:35
@ MlirDiagnosticRemark
Definition Diagnostics.h:36
@ MlirDiagnosticWarning
Definition Diagnostics.h:34
@ MlirDiagnosticError
Definition Diagnostics.h:33
MLIR_CAPI_EXPORTED MlirDiagnosticHandlerID mlirContextAttachDiagnosticHandler(MlirContext context, MlirDiagnosticHandler handler, void *userData, void(*deleteUserData)(void *))
Attaches the diagnostic handler to the context.
MLIR_CAPI_EXPORTED void mlirContextDetachDiagnosticHandler(MlirContext context, MlirDiagnosticHandlerID id)
Detaches an attached diagnostic handler from the context given its identifier.
uint64_t MlirDiagnosticHandlerID
Opaque identifier of a diagnostic handler, useful to detach a handler.
Definition Diagnostics.h:41
MLIR_CAPI_EXPORTED MlirTypeID mlirStringAttrGetTypeID(void)
Returns the typeID of a String attribute.
MLIR_CAPI_EXPORTED bool mlirAttributeIsAString(MlirAttribute attr)
Checks whether the given attribute is a string attribute.
MLIR_CAPI_EXPORTED MlirStringRef mlirStringAttrGetName(void)
@ MlirWalkPreOrder
Definition IR.h:929
@ MlirWalkPostOrder
Definition IR.h:930
MLIR_CAPI_EXPORTED bool mlirLocationIsAUnknown(MlirLocation location)
Checks whether the given location is an Unknown.
Definition IR.cpp:428
MLIR_CAPI_EXPORTED MlirTypeID mlirLocationUnknownGetTypeID(void)
TypeID Getter for Unknown.
Definition IR.cpp:424
MLIR_CAPI_EXPORTED void mlirDialectRegistryDestroy(MlirDialectRegistry registry)
Takes a dialect registry owned by the caller and destroys it.
Definition IR.cpp:166
MLIR_CAPI_EXPORTED bool mlirLocationIsAFileLineColRange(MlirLocation location)
Checks whether the given location is an FileLineColRange.
Definition IR.cpp:338
MLIR_CAPI_EXPORTED void mlirAttributePrint(MlirAttribute attr, MlirStringCallback callback, void *userData)
Prints a location by sending chunks of the string representation and forwarding userData to callback`...
Definition IR.cpp:1376
@ MlirWalkResultInterrupt
Definition IR.h:923
@ MlirWalkResultSkip
Definition IR.h:924
@ MlirWalkResultAdvance
Definition IR.h:922
MLIR_CAPI_EXPORTED bool mlirLocationIsACallSite(MlirLocation location)
Checks whether the given location is an CallSite.
Definition IR.cpp:360
static bool mlirBlockIsNull(MlirBlock block)
Checks whether a block is null.
Definition IR.h:1021
MLIR_CAPI_EXPORTED bool mlirLocationIsAFused(MlirLocation location)
Checks whether the given location is an Fused.
Definition IR.cpp:392
MLIR_CAPI_EXPORTED void mlirSymbolTableDestroy(MlirSymbolTable symbolTable)
Destroys the symbol table created with mlirSymbolTableCreate.
Definition IR.cpp:1427
MLIR_CAPI_EXPORTED bool mlirLocationIsAName(MlirLocation location)
Checks whether the given location is an Name.
Definition IR.cpp:416
static bool mlirDialectRegistryIsNull(MlirDialectRegistry registry)
Checks if the dialect registry is null.
Definition IR.h:266
MLIR_CAPI_EXPORTED MlirTypeID mlirLocationFileLineColRangeGetTypeID(void)
TypeID Getter for FileLineColRange.
Definition IR.cpp:334
static bool mlirRegionIsNull(MlirRegion region)
Checks whether a region is null.
Definition IR.h:960
MLIR_CAPI_EXPORTED MlirTypeID mlirLocationCallSiteGetTypeID(void)
TypeID Getter for CallSite.
Definition IR.cpp:356
MLIR_CAPI_EXPORTED MlirDialectRegistry mlirDialectRegistryCreate(void)
Creates a dialect registry and transfers its ownership to the caller.
Definition IR.cpp:162
MLIR_CAPI_EXPORTED MlirTypeID mlirLocationNameGetTypeID(void)
TypeID Getter for Name.
Definition IR.cpp:414
MLIR_CAPI_EXPORTED MlirTypeID mlirLocationFusedGetTypeID(void)
TypeID Getter for Fused.
Definition IR.cpp:390
MLIR_CAPI_EXPORTED void mlirValuePrint(MlirValue value, MlirStringCallback callback, void *userData)
Prints a block by sending chunks of the string representation and forwarding userData to callback`.
Definition IR.cpp:1224
@ MlirRemarkFileFormatBitstream
Definition Remarks.h:64
@ MlirRemarkFileFormatYAML
Definition Remarks.h:63
@ MlirRemarkPolicyFinal
Postpone remarks and report the final set when the engine is finalized, grouping related remarks unde...
Definition Remarks.h:57
@ MlirRemarkPolicyAll
Report every remark as it is emitted.
Definition Remarks.h:54
@ MlirRemarkKindFailure
The optimization was attempted but failed.
Definition Remarks.h:45
@ MlirRemarkKindAnalysis
Informational context without a pass/fail outcome.
Definition Remarks.h:47
@ MlirRemarkKindMissed
A profitable optimization opportunity was found but not applied.
Definition Remarks.h:43
@ MlirRemarkKindUnknown
Definition Remarks.h:39
@ MlirRemarkKindPassed
An optimization was applied.
Definition Remarks.h:41
static MlirStringRef mlirStringRefCreate(const char *str, size_t length)
Constructs a string reference from the pointer and length.
Definition Support.h:87
#define MLIR_PYTHON_API_EXPORTED
Definition Support.h:49
MLIR_PYTHON_API_EXPORTED MlirValue getUniqueResult(MlirOperation operation)
Definition IRCore.cpp:1527
PyOperationEquivalenceFlags
Flags controlling structural operation equivalence and hashing.
Definition IRCore.h:411
MLIR_PYTHON_API_EXPORTED void populateRoot(nanobind::module_ &m)
MLIR_PYTHON_API_EXPORTED void populateIRRemarks(nanobind::module_ &m)
PyObjectRef< PyMlirContext > PyMlirContextRef
Wrapper around MlirContext.
Definition IRCore.h:231
PyObjectRef< PyOperation > PyOperationRef
Definition IRCore.h:723
MlirStringRef toMlirStringRef(const std::string &s)
Definition IRCore.h:1554
PyRemarkKind
The kinds, emitting policies and file formats of the remark engine (mlir-c/Remarks....
Definition IRCore.h:212
PyObjectRef< PyModule > PyModuleRef
Definition IRCore.h:630
MlirBlock MLIR_PYTHON_API_EXPORTED createBlock(const nanobind::typed< nanobind::sequence, PyType > &pyArgTypes, const std::optional< nanobind::typed< nanobind::sequence, PyLocation > > &pyArgLocs)
Create a block, using the current location context if no locations are specified.
PyWalkOrder
Traversal order for operation walk.
Definition IRCore.h:404
MLIR_PYTHON_API_EXPORTED void populateIRCore(nanobind::module_ &m)
nanobind::object classmethod(Func f, Args... args)
Helper for creating an @classmethod.
Definition IRCore.h:2093
Include the generated interface declarations.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
An opaque reference to a diagnostic, always owned by the diagnostics engine (context).
Definition Diagnostics.h:26
A logical result value, essentially a boolean with named states.
Definition Support.h:121
Named MLIR attribute.
Definition IR.h:77
An opaque reference to a remark.
Definition Remarks.h:32
A pointer to a sized fragment of a string, not necessarily null-terminated.
Definition Support.h:78
Accumulates into a python string from a method that accepts an MlirStringCallback.
MlirStringCallback getCallback()
MLIRError(std::string message, std::vector< PyDiagnostic::DiagnosticInfo > &&errorDiagnostics={})
Definition IRCore.h:1537
std::vector< PyDiagnostic::DiagnosticInfo > errorDiagnostics
Definition IRCore.h:1547
const char * what() const noexcept override
Definition IRCore.h:1541
static bool dunderContains(const std::string &attributeKind)
Definition IRCore.cpp:146
static nanobind::callable dunderGetItemNamed(const std::string &attributeKind)
Definition IRCore.cpp:151
static void dunderSetItemNamed(const std::string &attributeKind, nanobind::callable func, bool replace, bool allow_existing)
Definition IRCore.cpp:158
Wrapper for the global LLVM debugging flag.
Definition IRCore.h:55
static void set(nanobind::object &o, bool enable)
Definition IRCore.cpp:108
std::vector< PyDiagnostic::DiagnosticInfo > take()
Definition IRCore.h:537