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