brintos

brintos / llvm-project-archived public Read only

0
0
Text · 49.4 KiB · e706be3 Raw
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