1349 lines · c
1//===- IRModules.h - IR Submodules of pybind module -----------------------===//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-exception6//7// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception8//===----------------------------------------------------------------------===//9 10#ifndef MLIR_BINDINGS_PYTHON_IRMODULES_H11#define MLIR_BINDINGS_PYTHON_IRMODULES_H12 13#include <optional>14#include <sstream>15#include <utility>16#include <vector>17 18#include "Globals.h"19#include "NanobindUtils.h"20#include "mlir-c/AffineExpr.h"21#include "mlir-c/AffineMap.h"22#include "mlir-c/BuiltinAttributes.h"23#include "mlir-c/Diagnostics.h"24#include "mlir-c/IR.h"25#include "mlir-c/IntegerSet.h"26#include "mlir-c/Transforms.h"27#include "mlir/Bindings/Python/Nanobind.h"28#include "mlir/Bindings/Python/NanobindAdaptors.h"29#include "llvm/ADT/DenseMap.h"30#include "llvm/Support/ThreadPool.h"31 32namespace mlir {33namespace python {34 35class PyBlock;36class PyDiagnostic;37class PyDiagnosticHandler;38class PyInsertionPoint;39class PyLocation;40class DefaultingPyLocation;41class PyMlirContext;42class DefaultingPyMlirContext;43class PyModule;44class PyOperation;45class PyOperationBase;46class PyType;47class PySymbolTable;48class PyValue;49 50/// Template for a reference to a concrete type which captures a python51/// reference to its underlying python object.52template <typename T>53class PyObjectRef {54public:55 PyObjectRef(T *referrent, nanobind::object object)56 : referrent(referrent), object(std::move(object)) {57 assert(this->referrent &&58 "cannot construct PyObjectRef with null referrent");59 assert(this->object && "cannot construct PyObjectRef with null object");60 }61 PyObjectRef(PyObjectRef &&other) noexcept62 : referrent(other.referrent), object(std::move(other.object)) {63 other.referrent = nullptr;64 assert(!other.object);65 }66 PyObjectRef(const PyObjectRef &other)67 : referrent(other.referrent), object(other.object /* copies */) {}68 ~PyObjectRef() = default;69 70 int getRefCount() {71 if (!object)72 return 0;73 return Py_REFCNT(object.ptr());74 }75 76 /// Releases the object held by this instance, returning it.77 /// This is the proper thing to return from a function that wants to return78 /// the reference. Note that this does not work from initializers.79 nanobind::object releaseObject() {80 assert(referrent && object);81 referrent = nullptr;82 auto stolen = std::move(object);83 return stolen;84 }85 86 T *get() { return referrent; }87 T *operator->() {88 assert(referrent && object);89 return referrent;90 }91 nanobind::object getObject() {92 assert(referrent && object);93 return object;94 }95 operator bool() const { return referrent && object; }96 97 using NBTypedT = nanobind::typed<nanobind::object, T>;98 99private:100 T *referrent;101 nanobind::object object;102};103 104/// Tracks an entry in the thread context stack. New entries are pushed onto105/// here for each with block that activates a new InsertionPoint, Context or106/// Location.107///108/// Pushing either a Location or InsertionPoint also pushes its associated109/// Context. Pushing a Context will not modify the Location or InsertionPoint110/// unless if they are from a different context, in which case, they are111/// cleared.112class PyThreadContextEntry {113public:114 enum class FrameKind {115 Context,116 InsertionPoint,117 Location,118 };119 120 PyThreadContextEntry(FrameKind frameKind, nanobind::object context,121 nanobind::object insertionPoint,122 nanobind::object location)123 : context(std::move(context)), insertionPoint(std::move(insertionPoint)),124 location(std::move(location)), frameKind(frameKind) {}125 126 /// Gets the top of stack context and return nullptr if not defined.127 static PyMlirContext *getDefaultContext();128 129 /// Gets the top of stack insertion point and return nullptr if not defined.130 static PyInsertionPoint *getDefaultInsertionPoint();131 132 /// Gets the top of stack location and returns nullptr if not defined.133 static PyLocation *getDefaultLocation();134 135 PyMlirContext *getContext();136 PyInsertionPoint *getInsertionPoint();137 PyLocation *getLocation();138 FrameKind getFrameKind() { return frameKind; }139 140 /// Stack management.141 static PyThreadContextEntry *getTopOfStack();142 static nanobind::object pushContext(nanobind::object context);143 static void popContext(PyMlirContext &context);144 static nanobind::object pushInsertionPoint(nanobind::object insertionPoint);145 static void popInsertionPoint(PyInsertionPoint &insertionPoint);146 static nanobind::object pushLocation(nanobind::object location);147 static void popLocation(PyLocation &location);148 149 /// Gets the thread local stack.150 static std::vector<PyThreadContextEntry> &getStack();151 152private:153 static void push(FrameKind frameKind, nanobind::object context,154 nanobind::object insertionPoint, nanobind::object location);155 156 /// An object reference to the PyContext.157 nanobind::object context;158 /// An object reference to the current insertion point.159 nanobind::object insertionPoint;160 /// An object reference to the current location.161 nanobind::object location;162 // The kind of push that was performed.163 FrameKind frameKind;164};165 166/// Wrapper around MlirLlvmThreadPool167/// Python object owns the C++ thread pool168class PyThreadPool {169public:170 PyThreadPool() {171 ownedThreadPool = std::make_unique<llvm::DefaultThreadPool>();172 }173 PyThreadPool(const PyThreadPool &) = delete;174 PyThreadPool(PyThreadPool &&) = delete;175 176 int getMaxConcurrency() const { return ownedThreadPool->getMaxConcurrency(); }177 MlirLlvmThreadPool get() { return wrap(ownedThreadPool.get()); }178 179 std::string _mlir_thread_pool_ptr() const {180 std::stringstream ss;181 ss << ownedThreadPool.get();182 return ss.str();183 }184 185private:186 std::unique_ptr<llvm::ThreadPoolInterface> ownedThreadPool;187};188 189/// Wrapper around MlirContext.190using PyMlirContextRef = PyObjectRef<PyMlirContext>;191class PyMlirContext {192public:193 PyMlirContext() = delete;194 PyMlirContext(MlirContext context);195 PyMlirContext(const PyMlirContext &) = delete;196 PyMlirContext(PyMlirContext &&) = delete;197 198 /// Returns a context reference for the singleton PyMlirContext wrapper for199 /// the given context.200 static PyMlirContextRef forContext(MlirContext context);201 ~PyMlirContext();202 203 /// Accesses the underlying MlirContext.204 MlirContext get() { return context; }205 206 /// Gets a strong reference to this context, which will ensure it is kept207 /// alive for the life of the reference.208 PyMlirContextRef getRef() {209 return PyMlirContextRef(this, nanobind::cast(this));210 }211 212 /// Gets a capsule wrapping the void* within the MlirContext.213 nanobind::object getCapsule();214 215 /// Creates a PyMlirContext from the MlirContext wrapped by a capsule.216 /// Note that PyMlirContext instances are uniqued, so the returned object217 /// may be a pre-existing object. Ownership of the underlying MlirContext218 /// is taken by calling this function.219 static nanobind::object createFromCapsule(nanobind::object capsule);220 221 /// Gets the count of live context objects. Used for testing.222 static size_t getLiveCount();223 224 /// Gets the count of live modules associated with this context.225 /// Used for testing.226 size_t getLiveModuleCount();227 228 /// Enter and exit the context manager.229 static nanobind::object contextEnter(nanobind::object context);230 void contextExit(const nanobind::object &excType,231 const nanobind::object &excVal,232 const nanobind::object &excTb);233 234 /// Attaches a Python callback as a diagnostic handler, returning a235 /// registration object (internally a PyDiagnosticHandler).236 nanobind::object attachDiagnosticHandler(nanobind::object callback);237 238 /// Controls whether error diagnostics should be propagated to diagnostic239 /// handlers, instead of being captured by `ErrorCapture`.240 void setEmitErrorDiagnostics(bool value) { emitErrorDiagnostics = value; }241 bool getEmitErrorDiagnostics() { return emitErrorDiagnostics; }242 struct ErrorCapture;243 244private:245 // Interns the mapping of live MlirContext::ptr to PyMlirContext instances,246 // preserving the relationship that an MlirContext maps to a single247 // PyMlirContext wrapper. This could be replaced in the future with an248 // extension mechanism on the MlirContext for stashing user pointers.249 // Note that this holds a handle, which does not imply ownership.250 // Mappings will be removed when the context is destructed.251 using LiveContextMap = llvm::DenseMap<void *, PyMlirContext *>;252 static nanobind::ft_mutex live_contexts_mutex;253 static LiveContextMap &getLiveContexts();254 255 // Interns all live modules associated with this context. Modules tracked256 // in this map are valid. When a module is invalidated, it is removed257 // from this map, and while it still exists as an instance, any258 // attempt to access it will raise an error.259 using LiveModuleMap =260 llvm::DenseMap<const void *, std::pair<nanobind::handle, PyModule *>>;261 LiveModuleMap liveModules;262 263 bool emitErrorDiagnostics = false;264 265 MlirContext context;266 friend class PyModule;267 friend class PyOperation;268};269 270/// Used in function arguments when None should resolve to the current context271/// manager set instance.272class DefaultingPyMlirContext273 : public Defaulting<DefaultingPyMlirContext, PyMlirContext> {274public:275 using Defaulting::Defaulting;276 static constexpr const char kTypeDescription[] = "Context";277 static PyMlirContext &resolve();278};279 280/// Base class for all objects that directly or indirectly depend on an281/// MlirContext. The lifetime of the context will extend at least to the282/// lifetime of these instances.283/// Immutable objects that depend on a context extend this directly.284class BaseContextObject {285public:286 BaseContextObject(PyMlirContextRef ref) : contextRef(std::move(ref)) {287 assert(this->contextRef &&288 "context object constructed with null context ref");289 }290 291 /// Accesses the context reference.292 PyMlirContextRef &getContext() { return contextRef; }293 294private:295 PyMlirContextRef contextRef;296};297 298/// Wrapper around an MlirLocation.299class PyLocation : public BaseContextObject {300public:301 PyLocation(PyMlirContextRef contextRef, MlirLocation loc)302 : BaseContextObject(std::move(contextRef)), loc(loc) {}303 304 operator MlirLocation() const { return loc; }305 MlirLocation get() const { return loc; }306 307 /// Enter and exit the context manager.308 static nanobind::object contextEnter(nanobind::object location);309 void contextExit(const nanobind::object &excType,310 const nanobind::object &excVal,311 const nanobind::object &excTb);312 313 /// Gets a capsule wrapping the void* within the MlirLocation.314 nanobind::object getCapsule();315 316 /// Creates a PyLocation from the MlirLocation wrapped by a capsule.317 /// Note that PyLocation instances are uniqued, so the returned object318 /// may be a pre-existing object. Ownership of the underlying MlirLocation319 /// is taken by calling this function.320 static PyLocation createFromCapsule(nanobind::object capsule);321 322private:323 MlirLocation loc;324};325 326/// Python class mirroring the C MlirDiagnostic struct. Note that these structs327/// are only valid for the duration of a diagnostic callback and attempting328/// to access them outside of that will raise an exception. This applies to329/// nested diagnostics (in the notes) as well.330class PyDiagnostic {331public:332 PyDiagnostic(MlirDiagnostic diagnostic) : diagnostic(diagnostic) {}333 void invalidate();334 bool isValid() { return valid; }335 MlirDiagnosticSeverity getSeverity();336 PyLocation getLocation();337 nanobind::str getMessage();338 nanobind::tuple getNotes();339 340 /// Materialized diagnostic information. This is safe to access outside the341 /// diagnostic callback.342 struct DiagnosticInfo {343 MlirDiagnosticSeverity severity;344 PyLocation location;345 std::string message;346 std::vector<DiagnosticInfo> notes;347 };348 DiagnosticInfo getInfo();349 350private:351 MlirDiagnostic diagnostic;352 353 void checkValid();354 /// If notes have been materialized from the diagnostic, then this will355 /// be populated with the corresponding objects (all castable to356 /// PyDiagnostic).357 std::optional<nanobind::tuple> materializedNotes;358 bool valid = true;359};360 361/// Represents a diagnostic handler attached to the context. The handler's362/// callback will be invoked with PyDiagnostic instances until the detach()363/// method is called or the context is destroyed. A diagnostic handler can be364/// the subject of a `with` block, which will detach it when the block exits.365///366/// Since diagnostic handlers can call back into Python code which can do367/// unsafe things (i.e. recursively emitting diagnostics, raising exceptions,368/// etc), this is generally not deemed to be a great user-level API. Users369/// should generally use some form of DiagnosticCollector. If the handler raises370/// any exceptions, they will just be emitted to stderr and dropped.371///372/// The unique usage of this class means that its lifetime management is373/// different from most other parts of the API. Instances are always created374/// in an attached state and can transition to a detached state by either:375/// a) The context being destroyed and unregistering all handlers.376/// b) An explicit call to detach().377/// The object may remain live from a Python perspective for an arbitrary time378/// after detachment, but there is nothing the user can do with it (since there379/// is no way to attach an existing handler object).380class PyDiagnosticHandler {381public:382 PyDiagnosticHandler(MlirContext context, nanobind::object callback);383 ~PyDiagnosticHandler();384 385 bool isAttached() { return registeredID.has_value(); }386 bool getHadError() { return hadError; }387 388 /// Detaches the handler. Does nothing if not attached.389 void detach();390 391 nanobind::object contextEnter() { return nanobind::cast(this); }392 void contextExit(const nanobind::object &excType,393 const nanobind::object &excVal,394 const nanobind::object &excTb) {395 detach();396 }397 398private:399 MlirContext context;400 nanobind::object callback;401 std::optional<MlirDiagnosticHandlerID> registeredID;402 bool hadError = false;403 friend class PyMlirContext;404};405 406/// RAII object that captures any error diagnostics emitted to the provided407/// context.408struct PyMlirContext::ErrorCapture {409 ErrorCapture(PyMlirContextRef ctx)410 : ctx(ctx), handlerID(mlirContextAttachDiagnosticHandler(411 ctx->get(), handler, /*userData=*/this,412 /*deleteUserData=*/nullptr)) {}413 ~ErrorCapture() {414 mlirContextDetachDiagnosticHandler(ctx->get(), handlerID);415 assert(errors.empty() && "unhandled captured errors");416 }417 418 std::vector<PyDiagnostic::DiagnosticInfo> take() {419 return std::move(errors);420 };421 422private:423 PyMlirContextRef ctx;424 MlirDiagnosticHandlerID handlerID;425 std::vector<PyDiagnostic::DiagnosticInfo> errors;426 427 static MlirLogicalResult handler(MlirDiagnostic diag, void *userData);428};429 430/// Wrapper around an MlirDialect. This is exported as `DialectDescriptor` in431/// order to differentiate it from the `Dialect` base class which is extended by432/// plugins which extend dialect functionality through extension python code.433/// This should be seen as the "low-level" object and `Dialect` as the434/// high-level, user facing object.435class PyDialectDescriptor : public BaseContextObject {436public:437 PyDialectDescriptor(PyMlirContextRef contextRef, MlirDialect dialect)438 : BaseContextObject(std::move(contextRef)), dialect(dialect) {}439 440 MlirDialect get() { return dialect; }441 442private:443 MlirDialect dialect;444};445 446/// User-level object for accessing dialects with dotted syntax such as:447/// ctx.dialect.std448class PyDialects : public BaseContextObject {449public:450 PyDialects(PyMlirContextRef contextRef)451 : BaseContextObject(std::move(contextRef)) {}452 453 MlirDialect getDialectForKey(const std::string &key, bool attrError);454};455 456/// User-level dialect object. For dialects that have a registered extension,457/// this will be the base class of the extension dialect type. For un-extended,458/// objects of this type will be returned directly.459class PyDialect {460public:461 PyDialect(nanobind::object descriptor) : descriptor(std::move(descriptor)) {}462 463 nanobind::object getDescriptor() { return descriptor; }464 465private:466 nanobind::object descriptor;467};468 469/// Wrapper around an MlirDialectRegistry.470/// Upon construction, the Python wrapper takes ownership of the471/// underlying MlirDialectRegistry.472class PyDialectRegistry {473public:474 PyDialectRegistry() : registry(mlirDialectRegistryCreate()) {}475 PyDialectRegistry(MlirDialectRegistry registry) : registry(registry) {}476 ~PyDialectRegistry() {477 if (!mlirDialectRegistryIsNull(registry))478 mlirDialectRegistryDestroy(registry);479 }480 PyDialectRegistry(PyDialectRegistry &) = delete;481 PyDialectRegistry(PyDialectRegistry &&other) noexcept482 : registry(other.registry) {483 other.registry = {nullptr};484 }485 486 operator MlirDialectRegistry() const { return registry; }487 MlirDialectRegistry get() const { return registry; }488 489 nanobind::object getCapsule();490 static PyDialectRegistry createFromCapsule(nanobind::object capsule);491 492private:493 MlirDialectRegistry registry;494};495 496/// Used in function arguments when None should resolve to the current context497/// manager set instance.498class DefaultingPyLocation499 : public Defaulting<DefaultingPyLocation, PyLocation> {500public:501 using Defaulting::Defaulting;502 static constexpr const char kTypeDescription[] = "Location";503 static PyLocation &resolve();504 505 operator MlirLocation() const { return *get(); }506};507 508/// Wrapper around MlirModule.509/// This is the top-level, user-owned object that contains regions/ops/blocks.510class PyModule;511using PyModuleRef = PyObjectRef<PyModule>;512class PyModule : public BaseContextObject {513public:514 /// Returns a PyModule reference for the given MlirModule. This always returns515 /// a new object.516 static PyModuleRef forModule(MlirModule module);517 PyModule(PyModule &) = delete;518 PyModule(PyMlirContext &&) = delete;519 ~PyModule();520 521 /// Gets the backing MlirModule.522 MlirModule get() { return module; }523 524 /// Gets a strong reference to this module.525 PyModuleRef getRef() {526 return PyModuleRef(this, nanobind::borrow<nanobind::object>(handle));527 }528 529 /// Gets a capsule wrapping the void* within the MlirModule.530 /// Note that the module does not (yet) provide a corresponding factory for531 /// constructing from a capsule as that would require uniquing PyModule532 /// instances, which is not currently done.533 nanobind::object getCapsule();534 535 /// Creates a PyModule from the MlirModule wrapped by a capsule.536 /// Note this returns a new object BUT clearMlirModule() must be called to537 /// prevent double-frees (of the underlying mlir::Module).538 static nanobind::object createFromCapsule(nanobind::object capsule);539 540 void clearMlirModule() { module = {nullptr}; }541 542private:543 PyModule(PyMlirContextRef contextRef, MlirModule module);544 MlirModule module;545 nanobind::handle handle;546};547 548class PyAsmState;549 550/// Base class for PyOperation and PyOpView which exposes the primary, user551/// visible methods for manipulating it.552class PyOperationBase {553public:554 virtual ~PyOperationBase() = default;555 /// Implements the bound 'print' method and helps with others.556 void print(std::optional<int64_t> largeElementsLimit,557 std::optional<int64_t> largeResourceLimit, bool enableDebugInfo,558 bool prettyDebugInfo, bool printGenericOpForm, bool useLocalScope,559 bool useNameLocAsPrefix, bool assumeVerified,560 nanobind::object fileObject, bool binary, bool skipRegions);561 void print(PyAsmState &state, nanobind::object fileObject, bool binary);562 563 nanobind::object564 getAsm(bool binary, std::optional<int64_t> largeElementsLimit,565 std::optional<int64_t> largeResourceLimit, bool enableDebugInfo,566 bool prettyDebugInfo, bool printGenericOpForm, bool useLocalScope,567 bool useNameLocAsPrefix, bool assumeVerified, bool skipRegions);568 569 // Implement the bound 'writeBytecode' method.570 void writeBytecode(const nanobind::object &fileObject,571 std::optional<int64_t> bytecodeVersion);572 573 // Implement the walk method.574 void walk(std::function<MlirWalkResult(MlirOperation)> callback,575 MlirWalkOrder walkOrder);576 577 /// Moves the operation before or after the other operation.578 void moveAfter(PyOperationBase &other);579 void moveBefore(PyOperationBase &other);580 581 /// Given an operation 'other' that is within the same parent block, return582 /// whether the current operation is before 'other' in the operation list583 /// of the parent block.584 /// Note: This function has an average complexity of O(1), but worst case may585 /// take O(N) where N is the number of operations within the parent block.586 bool isBeforeInBlock(PyOperationBase &other);587 588 /// Verify the operation. Throws `MLIRError` if verification fails, and589 /// returns `true` otherwise.590 bool verify();591 592 /// Each must provide access to the raw Operation.593 virtual PyOperation &getOperation() = 0;594};595 596/// Wrapper around PyOperation.597/// Operations exist in either an attached (dependent) or detached (top-level)598/// state. In the detached state (as on creation), an operation is owned by599/// the creator and its lifetime extends either until its reference count600/// drops to zero or it is attached to a parent, at which point its lifetime601/// is bounded by its top-level parent reference.602class PyOperation;603class PyOpView;604using PyOperationRef = PyObjectRef<PyOperation>;605class PyOperation : public PyOperationBase, public BaseContextObject {606public:607 ~PyOperation() override;608 PyOperation &getOperation() override { return *this; }609 610 /// Returns a PyOperation for the given MlirOperation, optionally associating611 /// it with a parentKeepAlive.612 static PyOperationRef613 forOperation(PyMlirContextRef contextRef, MlirOperation operation,614 nanobind::object parentKeepAlive = nanobind::object());615 616 /// Creates a detached operation. The operation must not be associated with617 /// any existing live operation.618 static PyOperationRef619 createDetached(PyMlirContextRef contextRef, MlirOperation operation,620 nanobind::object parentKeepAlive = nanobind::object());621 622 /// Parses a source string (either text assembly or bytecode), creating a623 /// detached operation.624 static PyOperationRef parse(PyMlirContextRef contextRef,625 const std::string &sourceStr,626 const std::string &sourceName);627 628 /// Detaches the operation from its parent block and updates its state629 /// accordingly.630 void detachFromParent() {631 mlirOperationRemoveFromParent(getOperation());632 setDetached();633 parentKeepAlive = nanobind::object();634 }635 636 /// Gets the backing operation.637 operator MlirOperation() const { return get(); }638 MlirOperation get() const {639 checkValid();640 return operation;641 }642 643 PyOperationRef getRef() {644 return PyOperationRef(this, nanobind::borrow<nanobind::object>(handle));645 }646 647 bool isAttached() { return attached; }648 void setAttached(const nanobind::object &parent = nanobind::object()) {649 assert(!attached && "operation already attached");650 attached = true;651 }652 void setDetached() {653 assert(attached && "operation already detached");654 attached = false;655 }656 void checkValid() const;657 658 /// Gets the owning block or raises an exception if the operation has no659 /// owning block.660 PyBlock getBlock();661 662 /// Gets the parent operation or raises an exception if the operation has663 /// no parent.664 std::optional<PyOperationRef> getParentOperation();665 666 /// Gets a capsule wrapping the void* within the MlirOperation.667 nanobind::object getCapsule();668 669 /// Creates a PyOperation from the MlirOperation wrapped by a capsule.670 /// Ownership of the underlying MlirOperation is taken by calling this671 /// function.672 static nanobind::object createFromCapsule(const nanobind::object &capsule);673 674 /// Creates an operation. See corresponding python docstring.675 static nanobind::object676 create(std::string_view name, std::optional<std::vector<PyType *>> results,677 llvm::ArrayRef<MlirValue> operands,678 std::optional<nanobind::dict> attributes,679 std::optional<std::vector<PyBlock *>> successors, int regions,680 PyLocation &location, const nanobind::object &ip, bool inferType);681 682 /// Creates an OpView suitable for this operation.683 nanobind::object createOpView();684 685 /// Erases the underlying MlirOperation, removes its pointer from the686 /// parent context's live operations map, and sets the valid bit false.687 void erase();688 689 /// Invalidate the operation.690 void setInvalid() { valid = false; }691 692 /// Clones this operation.693 nanobind::object clone(const nanobind::object &ip);694 695 PyOperation(PyMlirContextRef contextRef, MlirOperation operation);696 697private:698 static PyOperationRef createInstance(PyMlirContextRef contextRef,699 MlirOperation operation,700 nanobind::object parentKeepAlive);701 702 MlirOperation operation;703 nanobind::handle handle;704 // Keeps the parent alive, regardless of whether it is an Operation or705 // Module.706 // TODO: As implemented, this facility is only sufficient for modeling the707 // trivial module parent back-reference. Generalize this to also account for708 // transitions from detached to attached and address TODOs in the709 // ir_operation.py regarding testing corresponding lifetime guarantees.710 nanobind::object parentKeepAlive;711 bool attached = true;712 bool valid = true;713 714 friend class PyOperationBase;715 friend class PySymbolTable;716};717 718/// A PyOpView is equivalent to the C++ "Op" wrappers: these are the basis for719/// providing more instance-specific accessors and serve as the base class for720/// custom ODS-style operation classes. Since this class is subclass on the721/// python side, it must present an __init__ method that operates in pure722/// python types.723class PyOpView : public PyOperationBase {724public:725 PyOpView(const nanobind::object &operationObject);726 PyOperation &getOperation() override { return operation; }727 728 nanobind::object getOperationObject() { return operationObject; }729 730 static nanobind::object731 buildGeneric(std::string_view name, std::tuple<int, bool> opRegionSpec,732 nanobind::object operandSegmentSpecObj,733 nanobind::object resultSegmentSpecObj,734 std::optional<nanobind::list> resultTypeList,735 nanobind::list operandList,736 std::optional<nanobind::dict> attributes,737 std::optional<std::vector<PyBlock *>> successors,738 std::optional<int> regions, PyLocation &location,739 const nanobind::object &maybeIp);740 741 /// Construct an instance of a class deriving from OpView, bypassing its742 /// `__init__` method. The derived class will typically define a constructor743 /// that provides a convenient builder, but we need to side-step this when744 /// constructing an `OpView` for an already-built operation.745 ///746 /// The caller is responsible for verifying that `operation` is a valid747 /// operation to construct `cls` with.748 static nanobind::object constructDerived(const nanobind::object &cls,749 const nanobind::object &operation);750 751private:752 PyOperation &operation; // For efficient, cast-free access from C++753 nanobind::object operationObject; // Holds the reference.754};755 756/// Wrapper around an MlirRegion.757/// Regions are managed completely by their containing operation. Unlike the758/// C++ API, the python API does not support detached regions.759class PyRegion {760public:761 PyRegion(PyOperationRef parentOperation, MlirRegion region)762 : parentOperation(std::move(parentOperation)), region(region) {763 assert(!mlirRegionIsNull(region) && "python region cannot be null");764 }765 operator MlirRegion() const { return region; }766 767 MlirRegion get() { return region; }768 PyOperationRef &getParentOperation() { return parentOperation; }769 770 void checkValid() { return parentOperation->checkValid(); }771 772private:773 PyOperationRef parentOperation;774 MlirRegion region;775};776 777/// Wrapper around an MlirAsmState.778class PyAsmState {779public:780 PyAsmState(MlirValue value, bool useLocalScope) {781 flags = mlirOpPrintingFlagsCreate();782 // The OpPrintingFlags are not exposed Python side, create locally and783 // associate lifetime with the state.784 if (useLocalScope)785 mlirOpPrintingFlagsUseLocalScope(flags);786 state = mlirAsmStateCreateForValue(value, flags);787 }788 789 PyAsmState(PyOperationBase &operation, bool useLocalScope) {790 flags = mlirOpPrintingFlagsCreate();791 // The OpPrintingFlags are not exposed Python side, create locally and792 // associate lifetime with the state.793 if (useLocalScope)794 mlirOpPrintingFlagsUseLocalScope(flags);795 state =796 mlirAsmStateCreateForOperation(operation.getOperation().get(), flags);797 }798 ~PyAsmState() { mlirOpPrintingFlagsDestroy(flags); }799 // Delete copy constructors.800 PyAsmState(PyAsmState &other) = delete;801 PyAsmState(const PyAsmState &other) = delete;802 803 MlirAsmState get() { return state; }804 805private:806 MlirAsmState state;807 MlirOpPrintingFlags flags;808};809 810/// Wrapper around an MlirBlock.811/// Blocks are managed completely by their containing operation. Unlike the812/// C++ API, the python API does not support detached blocks.813class PyBlock {814public:815 PyBlock(PyOperationRef parentOperation, MlirBlock block)816 : parentOperation(std::move(parentOperation)), block(block) {817 assert(!mlirBlockIsNull(block) && "python block cannot be null");818 }819 820 MlirBlock get() { return block; }821 PyOperationRef &getParentOperation() { return parentOperation; }822 823 void checkValid() { return parentOperation->checkValid(); }824 825 /// Gets a capsule wrapping the void* within the MlirBlock.826 nanobind::object getCapsule();827 828private:829 PyOperationRef parentOperation;830 MlirBlock block;831};832 833/// An insertion point maintains a pointer to a Block and a reference operation.834/// Calls to insert() will insert a new operation before the835/// reference operation. If the reference operation is null, then appends to836/// the end of the block.837class PyInsertionPoint {838public:839 /// Creates an insertion point positioned after the last operation in the840 /// block, but still inside the block.841 PyInsertionPoint(const PyBlock &block);842 /// Creates an insertion point positioned before a reference operation.843 PyInsertionPoint(PyOperationBase &beforeOperationBase);844 /// Creates an insertion point positioned before a reference operation.845 PyInsertionPoint(PyOperationRef beforeOperationRef);846 847 /// Shortcut to create an insertion point at the beginning of the block.848 static PyInsertionPoint atBlockBegin(PyBlock &block);849 /// Shortcut to create an insertion point before the block terminator.850 static PyInsertionPoint atBlockTerminator(PyBlock &block);851 /// Shortcut to create an insertion point to the node after the specified852 /// operation.853 static PyInsertionPoint after(PyOperationBase &op);854 855 /// Inserts an operation.856 void insert(PyOperationBase &operationBase);857 858 /// Enter and exit the context manager.859 static nanobind::object contextEnter(nanobind::object insertionPoint);860 void contextExit(const nanobind::object &excType,861 const nanobind::object &excVal,862 const nanobind::object &excTb);863 864 PyBlock &getBlock() { return block; }865 std::optional<PyOperationRef> &getRefOperation() { return refOperation; }866 867private:868 // Trampoline constructor that avoids null initializing members while869 // looking up parents.870 PyInsertionPoint(PyBlock block, std::optional<PyOperationRef> refOperation)871 : refOperation(std::move(refOperation)), block(std::move(block)) {}872 873 std::optional<PyOperationRef> refOperation;874 PyBlock block;875};876/// Wrapper around the generic MlirType.877/// The lifetime of a type is bound by the PyContext that created it.878class PyType : public BaseContextObject {879public:880 PyType(PyMlirContextRef contextRef, MlirType type)881 : BaseContextObject(std::move(contextRef)), type(type) {}882 bool operator==(const PyType &other) const;883 operator MlirType() const { return type; }884 MlirType get() const { return type; }885 886 /// Gets a capsule wrapping the void* within the MlirType.887 nanobind::object getCapsule();888 889 /// Creates a PyType from the MlirType wrapped by a capsule.890 /// Note that PyType instances are uniqued, so the returned object891 /// may be a pre-existing object. Ownership of the underlying MlirType892 /// is taken by calling this function.893 static PyType createFromCapsule(nanobind::object capsule);894 895 nanobind::object maybeDownCast();896 897private:898 MlirType type;899};900 901/// A TypeID provides an efficient and unique identifier for a specific C++902/// type. This allows for a C++ type to be compared, hashed, and stored in an903/// opaque context. This class wraps around the generic MlirTypeID.904class PyTypeID {905public:906 PyTypeID(MlirTypeID typeID) : typeID(typeID) {}907 // Note, this tests whether the underlying TypeIDs are the same,908 // not whether the wrapper MlirTypeIDs are the same, nor whether909 // the PyTypeID objects are the same (i.e., PyTypeID is a value type).910 bool operator==(const PyTypeID &other) const;911 operator MlirTypeID() const { return typeID; }912 MlirTypeID get() { return typeID; }913 914 /// Gets a capsule wrapping the void* within the MlirTypeID.915 nanobind::object getCapsule();916 917 /// Creates a PyTypeID from the MlirTypeID wrapped by a capsule.918 static PyTypeID createFromCapsule(nanobind::object capsule);919 920private:921 MlirTypeID typeID;922};923 924/// CRTP base classes for Python types that subclass Type and should be925/// castable from it (i.e. via something like IntegerType(t)).926/// By default, type class hierarchies are one level deep (i.e. a927/// concrete type class extends PyType); however, intermediate python-visible928/// base classes can be modeled by specifying a BaseTy.929template <typename DerivedTy, typename BaseTy = PyType>930class PyConcreteType : public BaseTy {931public:932 // Derived classes must define statics for:933 // IsAFunctionTy isaFunction934 // const char *pyClassName935 using ClassTy = nanobind::class_<DerivedTy, BaseTy>;936 using IsAFunctionTy = bool (*)(MlirType);937 using GetTypeIDFunctionTy = MlirTypeID (*)();938 static constexpr GetTypeIDFunctionTy getTypeIdFunction = nullptr;939 940 PyConcreteType() = default;941 PyConcreteType(PyMlirContextRef contextRef, MlirType t)942 : BaseTy(std::move(contextRef), t) {}943 PyConcreteType(PyType &orig)944 : PyConcreteType(orig.getContext(), castFrom(orig)) {}945 946 static MlirType castFrom(PyType &orig) {947 if (!DerivedTy::isaFunction(orig)) {948 auto origRepr =949 nanobind::cast<std::string>(nanobind::repr(nanobind::cast(orig)));950 throw nanobind::value_error((llvm::Twine("Cannot cast type to ") +951 DerivedTy::pyClassName + " (from " +952 origRepr + ")")953 .str()954 .c_str());955 }956 return orig;957 }958 959 static void bind(nanobind::module_ &m) {960 auto cls = ClassTy(m, DerivedTy::pyClassName);961 cls.def(nanobind::init<PyType &>(), nanobind::keep_alive<0, 1>(),962 nanobind::arg("cast_from_type"));963 cls.def_static(964 "isinstance",965 [](PyType &otherType) -> bool {966 return DerivedTy::isaFunction(otherType);967 },968 nanobind::arg("other"));969 cls.def_prop_ro_static(970 "static_typeid",971 [](nanobind::object & /*class*/) {972 if (DerivedTy::getTypeIdFunction)973 return PyTypeID(DerivedTy::getTypeIdFunction());974 throw nanobind::attribute_error(975 (DerivedTy::pyClassName + llvm::Twine(" has no typeid."))976 .str()977 .c_str());978 },979 nanobind::sig("def static_typeid(/) -> TypeID"));980 cls.def_prop_ro("typeid", [](PyType &self) {981 return nanobind::cast<PyTypeID>(nanobind::cast(self).attr("typeid"));982 });983 cls.def("__repr__", [](DerivedTy &self) {984 PyPrintAccumulator printAccum;985 printAccum.parts.append(DerivedTy::pyClassName);986 printAccum.parts.append("(");987 mlirTypePrint(self, printAccum.getCallback(), printAccum.getUserData());988 printAccum.parts.append(")");989 return printAccum.join();990 });991 992 if (DerivedTy::getTypeIdFunction) {993 PyGlobals::get().registerTypeCaster(994 DerivedTy::getTypeIdFunction(),995 nanobind::cast<nanobind::callable>(nanobind::cpp_function(996 [](PyType pyType) -> DerivedTy { return pyType; })));997 }998 999 DerivedTy::bindDerived(cls);1000 }1001 1002 /// Implemented by derived classes to add methods to the Python subclass.1003 static void bindDerived(ClassTy &m) {}1004};1005 1006/// Wrapper around the generic MlirAttribute.1007/// The lifetime of a type is bound by the PyContext that created it.1008class PyAttribute : public BaseContextObject {1009public:1010 PyAttribute(PyMlirContextRef contextRef, MlirAttribute attr)1011 : BaseContextObject(std::move(contextRef)), attr(attr) {}1012 bool operator==(const PyAttribute &other) const;1013 operator MlirAttribute() const { return attr; }1014 MlirAttribute get() const { return attr; }1015 1016 /// Gets a capsule wrapping the void* within the MlirAttribute.1017 nanobind::object getCapsule();1018 1019 /// Creates a PyAttribute from the MlirAttribute wrapped by a capsule.1020 /// Note that PyAttribute instances are uniqued, so the returned object1021 /// may be a pre-existing object. Ownership of the underlying MlirAttribute1022 /// is taken by calling this function.1023 static PyAttribute createFromCapsule(const nanobind::object &capsule);1024 1025 nanobind::object maybeDownCast();1026 1027private:1028 MlirAttribute attr;1029};1030 1031/// Represents a Python MlirNamedAttr, carrying an optional owned name.1032/// TODO: Refactor this and the C-API to be based on an Identifier owned1033/// by the context so as to avoid ownership issues here.1034class PyNamedAttribute {1035public:1036 /// Constructs a PyNamedAttr that retains an owned name. This should be1037 /// used in any code that originates an MlirNamedAttribute from a python1038 /// string.1039 /// The lifetime of the PyNamedAttr must extend to the lifetime of the1040 /// passed attribute.1041 PyNamedAttribute(MlirAttribute attr, std::string ownedName);1042 1043 MlirNamedAttribute namedAttr;1044 1045private:1046 // Since the MlirNamedAttr contains an internal pointer to the actual1047 // memory of the owned string, it must be heap allocated to remain valid.1048 // Otherwise, strings that fit within the small object optimization threshold1049 // will have their memory address change as the containing object is moved,1050 // resulting in an invalid aliased pointer.1051 std::unique_ptr<std::string> ownedName;1052};1053 1054/// CRTP base classes for Python attributes that subclass Attribute and should1055/// be castable from it (i.e. via something like StringAttr(attr)).1056/// By default, attribute class hierarchies are one level deep (i.e. a1057/// concrete attribute class extends PyAttribute); however, intermediate1058/// python-visible base classes can be modeled by specifying a BaseTy.1059template <typename DerivedTy, typename BaseTy = PyAttribute>1060class PyConcreteAttribute : public BaseTy {1061public:1062 // Derived classes must define statics for:1063 // IsAFunctionTy isaFunction1064 // const char *pyClassName1065 using ClassTy = nanobind::class_<DerivedTy, BaseTy>;1066 using IsAFunctionTy = bool (*)(MlirAttribute);1067 using GetTypeIDFunctionTy = MlirTypeID (*)();1068 static constexpr GetTypeIDFunctionTy getTypeIdFunction = nullptr;1069 1070 PyConcreteAttribute() = default;1071 PyConcreteAttribute(PyMlirContextRef contextRef, MlirAttribute attr)1072 : BaseTy(std::move(contextRef), attr) {}1073 PyConcreteAttribute(PyAttribute &orig)1074 : PyConcreteAttribute(orig.getContext(), castFrom(orig)) {}1075 1076 static MlirAttribute castFrom(PyAttribute &orig) {1077 if (!DerivedTy::isaFunction(orig)) {1078 auto origRepr =1079 nanobind::cast<std::string>(nanobind::repr(nanobind::cast(orig)));1080 throw nanobind::value_error((llvm::Twine("Cannot cast attribute to ") +1081 DerivedTy::pyClassName + " (from " +1082 origRepr + ")")1083 .str()1084 .c_str());1085 }1086 return orig;1087 }1088 1089 static void bind(nanobind::module_ &m, PyType_Slot *slots = nullptr) {1090 ClassTy cls;1091 if (slots) {1092 cls = ClassTy(m, DerivedTy::pyClassName, nanobind::type_slots(slots));1093 } else {1094 cls = ClassTy(m, DerivedTy::pyClassName);1095 }1096 cls.def(nanobind::init<PyAttribute &>(), nanobind::keep_alive<0, 1>(),1097 nanobind::arg("cast_from_attr"));1098 cls.def_static(1099 "isinstance",1100 [](PyAttribute &otherAttr) -> bool {1101 return DerivedTy::isaFunction(otherAttr);1102 },1103 nanobind::arg("other"));1104 cls.def_prop_ro(1105 "type",1106 [](PyAttribute &attr) -> nanobind::typed<nanobind::object, PyType> {1107 return PyType(attr.getContext(), mlirAttributeGetType(attr))1108 .maybeDownCast();1109 });1110 cls.def_prop_ro_static(1111 "static_typeid",1112 [](nanobind::object & /*class*/) -> PyTypeID {1113 if (DerivedTy::getTypeIdFunction)1114 return PyTypeID(DerivedTy::getTypeIdFunction());1115 throw nanobind::attribute_error(1116 (DerivedTy::pyClassName + llvm::Twine(" has no typeid."))1117 .str()1118 .c_str());1119 },1120 nanobind::sig("def static_typeid(/) -> TypeID"));1121 cls.def_prop_ro("typeid", [](PyAttribute &self) {1122 return nanobind::cast<PyTypeID>(nanobind::cast(self).attr("typeid"));1123 });1124 cls.def("__repr__", [](DerivedTy &self) {1125 PyPrintAccumulator printAccum;1126 printAccum.parts.append(DerivedTy::pyClassName);1127 printAccum.parts.append("(");1128 mlirAttributePrint(self, printAccum.getCallback(),1129 printAccum.getUserData());1130 printAccum.parts.append(")");1131 return printAccum.join();1132 });1133 1134 if (DerivedTy::getTypeIdFunction) {1135 PyGlobals::get().registerTypeCaster(1136 DerivedTy::getTypeIdFunction(),1137 nanobind::cast<nanobind::callable>(1138 nanobind::cpp_function([](PyAttribute pyAttribute) -> DerivedTy {1139 return pyAttribute;1140 })));1141 }1142 1143 DerivedTy::bindDerived(cls);1144 }1145 1146 /// Implemented by derived classes to add methods to the Python subclass.1147 static void bindDerived(ClassTy &m) {}1148};1149 1150class PyStringAttribute : public PyConcreteAttribute<PyStringAttribute> {1151public:1152 static constexpr IsAFunctionTy isaFunction = mlirAttributeIsAString;1153 static constexpr const char *pyClassName = "StringAttr";1154 using PyConcreteAttribute::PyConcreteAttribute;1155 static constexpr GetTypeIDFunctionTy getTypeIdFunction =1156 mlirStringAttrGetTypeID;1157 1158 static void bindDerived(ClassTy &c);1159};1160 1161/// Wrapper around the generic MlirValue.1162/// Values are managed completely by the operation that resulted in their1163/// definition. For op result value, this is the operation that defines the1164/// value. For block argument values, this is the operation that contains the1165/// block to which the value is an argument (blocks cannot be detached in Python1166/// bindings so such operation always exists).1167class PyValue {1168public:1169 // The virtual here is "load bearing" in that it enables RTTI1170 // for PyConcreteValue CRTP classes that support maybeDownCast.1171 // See PyValue::maybeDownCast.1172 virtual ~PyValue() = default;1173 PyValue(PyOperationRef parentOperation, MlirValue value)1174 : parentOperation(std::move(parentOperation)), value(value) {}1175 operator MlirValue() const { return value; }1176 1177 MlirValue get() { return value; }1178 PyOperationRef &getParentOperation() { return parentOperation; }1179 1180 void checkValid() { return parentOperation->checkValid(); }1181 1182 /// Gets a capsule wrapping the void* within the MlirValue.1183 nanobind::object getCapsule();1184 1185 nanobind::object maybeDownCast();1186 1187 /// Creates a PyValue from the MlirValue wrapped by a capsule. Ownership of1188 /// the underlying MlirValue is still tied to the owning operation.1189 static PyValue createFromCapsule(nanobind::object capsule);1190 1191private:1192 PyOperationRef parentOperation;1193 MlirValue value;1194};1195 1196/// Wrapper around MlirAffineExpr. Affine expressions are owned by the context.1197class PyAffineExpr : public BaseContextObject {1198public:1199 PyAffineExpr(PyMlirContextRef contextRef, MlirAffineExpr affineExpr)1200 : BaseContextObject(std::move(contextRef)), affineExpr(affineExpr) {}1201 bool operator==(const PyAffineExpr &other) const;1202 operator MlirAffineExpr() const { return affineExpr; }1203 MlirAffineExpr get() const { return affineExpr; }1204 1205 /// Gets a capsule wrapping the void* within the MlirAffineExpr.1206 nanobind::object getCapsule();1207 1208 /// Creates a PyAffineExpr from the MlirAffineExpr wrapped by a capsule.1209 /// Note that PyAffineExpr instances are uniqued, so the returned object1210 /// may be a pre-existing object. Ownership of the underlying MlirAffineExpr1211 /// is taken by calling this function.1212 static PyAffineExpr createFromCapsule(const nanobind::object &capsule);1213 1214 PyAffineExpr add(const PyAffineExpr &other) const;1215 PyAffineExpr mul(const PyAffineExpr &other) const;1216 PyAffineExpr floorDiv(const PyAffineExpr &other) const;1217 PyAffineExpr ceilDiv(const PyAffineExpr &other) const;1218 PyAffineExpr mod(const PyAffineExpr &other) const;1219 1220private:1221 MlirAffineExpr affineExpr;1222};1223 1224class PyAffineMap : public BaseContextObject {1225public:1226 PyAffineMap(PyMlirContextRef contextRef, MlirAffineMap affineMap)1227 : BaseContextObject(std::move(contextRef)), affineMap(affineMap) {}1228 bool operator==(const PyAffineMap &other) const;1229 operator MlirAffineMap() const { return affineMap; }1230 MlirAffineMap get() const { return affineMap; }1231 1232 /// Gets a capsule wrapping the void* within the MlirAffineMap.1233 nanobind::object getCapsule();1234 1235 /// Creates a PyAffineMap from the MlirAffineMap wrapped by a capsule.1236 /// Note that PyAffineMap instances are uniqued, so the returned object1237 /// may be a pre-existing object. Ownership of the underlying MlirAffineMap1238 /// is taken by calling this function.1239 static PyAffineMap createFromCapsule(const nanobind::object &capsule);1240 1241private:1242 MlirAffineMap affineMap;1243};1244 1245class PyIntegerSet : public BaseContextObject {1246public:1247 PyIntegerSet(PyMlirContextRef contextRef, MlirIntegerSet integerSet)1248 : BaseContextObject(std::move(contextRef)), integerSet(integerSet) {}1249 bool operator==(const PyIntegerSet &other) const;1250 operator MlirIntegerSet() const { return integerSet; }1251 MlirIntegerSet get() const { return integerSet; }1252 1253 /// Gets a capsule wrapping the void* within the MlirIntegerSet.1254 nanobind::object getCapsule();1255 1256 /// Creates a PyIntegerSet from the MlirAffineMap wrapped by a capsule.1257 /// Note that PyIntegerSet instances may be uniqued, so the returned object1258 /// may be a pre-existing object. Integer sets are owned by the context.1259 static PyIntegerSet createFromCapsule(const nanobind::object &capsule);1260 1261private:1262 MlirIntegerSet integerSet;1263};1264 1265/// Bindings for MLIR symbol tables.1266class PySymbolTable {1267public:1268 /// Constructs a symbol table for the given operation.1269 explicit PySymbolTable(PyOperationBase &operation);1270 1271 /// Destroys the symbol table.1272 ~PySymbolTable() { mlirSymbolTableDestroy(symbolTable); }1273 1274 /// Returns the symbol (opview) with the given name, throws if there is no1275 /// such symbol in the table.1276 nanobind::object dunderGetItem(const std::string &name);1277 1278 /// Removes the given operation from the symbol table and erases it.1279 void erase(PyOperationBase &symbol);1280 1281 /// Removes the operation with the given name from the symbol table and erases1282 /// it, throws if there is no such symbol in the table.1283 void dunderDel(const std::string &name);1284 1285 /// Inserts the given operation into the symbol table. The operation must have1286 /// the symbol trait.1287 PyStringAttribute insert(PyOperationBase &symbol);1288 1289 /// Gets and sets the name of a symbol op.1290 static PyStringAttribute getSymbolName(PyOperationBase &symbol);1291 static void setSymbolName(PyOperationBase &symbol, const std::string &name);1292 1293 /// Gets and sets the visibility of a symbol op.1294 static PyStringAttribute getVisibility(PyOperationBase &symbol);1295 static void setVisibility(PyOperationBase &symbol,1296 const std::string &visibility);1297 1298 /// Replaces all symbol uses within an operation. See the API1299 /// mlirSymbolTableReplaceAllSymbolUses for all caveats.1300 static void replaceAllSymbolUses(const std::string &oldSymbol,1301 const std::string &newSymbol,1302 PyOperationBase &from);1303 1304 /// Walks all symbol tables under and including 'from'.1305 static void walkSymbolTables(PyOperationBase &from, bool allSymUsesVisible,1306 nanobind::object callback);1307 1308 /// Casts the bindings class into the C API structure.1309 operator MlirSymbolTable() { return symbolTable; }1310 1311private:1312 PyOperationRef operation;1313 MlirSymbolTable symbolTable;1314};1315 1316/// Custom exception that allows access to error diagnostic information. This is1317/// converted to the `ir.MLIRError` python exception when thrown.1318struct MLIRError {1319 MLIRError(llvm::Twine message,1320 std::vector<PyDiagnostic::DiagnosticInfo> &&errorDiagnostics = {})1321 : message(message.str()), errorDiagnostics(std::move(errorDiagnostics)) {}1322 std::string message;1323 std::vector<PyDiagnostic::DiagnosticInfo> errorDiagnostics;1324};1325 1326void populateIRAffine(nanobind::module_ &m);1327void populateIRAttributes(nanobind::module_ &m);1328void populateIRCore(nanobind::module_ &m);1329void populateIRInterfaces(nanobind::module_ &m);1330void populateIRTypes(nanobind::module_ &m);1331 1332} // namespace python1333} // namespace mlir1334 1335namespace nanobind {1336namespace detail {1337 1338template <>1339struct type_caster<mlir::python::DefaultingPyMlirContext>1340 : MlirDefaultingCaster<mlir::python::DefaultingPyMlirContext> {};1341template <>1342struct type_caster<mlir::python::DefaultingPyLocation>1343 : MlirDefaultingCaster<mlir::python::DefaultingPyLocation> {};1344 1345} // namespace detail1346} // namespace nanobind1347 1348#endif // MLIR_BINDINGS_PYTHON_IRMODULES_H1349