1 //===- IRModules.cpp - 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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "IRModule.h"
10 
11 #include "Globals.h"
12 #include "PybindUtils.h"
13 
14 #include "mlir-c/Bindings/Python/Interop.h"
15 #include "mlir-c/BuiltinAttributes.h"
16 #include "mlir-c/BuiltinTypes.h"
17 #include "mlir-c/Debug.h"
18 #include "mlir-c/IR.h"
19 #include "mlir-c/Registration.h"
20 #include "llvm/ADT/ArrayRef.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include <pybind11/stl.h>
23 
24 #include <utility>
25 
26 namespace py = pybind11;
27 using namespace mlir;
28 using namespace mlir::python;
29 
30 using llvm::SmallVector;
31 using llvm::StringRef;
32 using llvm::Twine;
33 
34 //------------------------------------------------------------------------------
35 // Docstrings (trivial, non-duplicated docstrings are included inline).
36 //------------------------------------------------------------------------------
37 
38 static const char kContextParseTypeDocstring[] =
39     R"(Parses the assembly form of a type.
40 
41 Returns a Type object or raises a ValueError if the type cannot be parsed.
42 
43 See also: https://mlir.llvm.org/docs/LangRef/#type-system
44 )";
45 
46 static const char kContextGetCallSiteLocationDocstring[] =
47     R"(Gets a Location representing a caller and callsite)";
48 
49 static const char kContextGetFileLocationDocstring[] =
50     R"(Gets a Location representing a file, line and column)";
51 
52 static const char kContextGetFusedLocationDocstring[] =
53     R"(Gets a Location representing a fused location with optional metadata)";
54 
55 static const char kContextGetNameLocationDocString[] =
56     R"(Gets a Location representing a named location with optional child location)";
57 
58 static const char kModuleParseDocstring[] =
59     R"(Parses a module's assembly format from a string.
60 
61 Returns a new MlirModule or raises a ValueError if the parsing fails.
62 
63 See also: https://mlir.llvm.org/docs/LangRef/
64 )";
65 
66 static const char kOperationCreateDocstring[] =
67     R"(Creates a new operation.
68 
69 Args:
70   name: Operation name (e.g. "dialect.operation").
71   results: Sequence of Type representing op result types.
72   attributes: Dict of str:Attribute.
73   successors: List of Block for the operation's successors.
74   regions: Number of regions to create.
75   location: A Location object (defaults to resolve from context manager).
76   ip: An InsertionPoint (defaults to resolve from context manager or set to
77     False to disable insertion, even with an insertion point set in the
78     context manager).
79 Returns:
80   A new "detached" Operation object. Detached operations can be added
81   to blocks, which causes them to become "attached."
82 )";
83 
84 static const char kOperationPrintDocstring[] =
85     R"(Prints the assembly form of the operation to a file like object.
86 
87 Args:
88   file: The file like object to write to. Defaults to sys.stdout.
89   binary: Whether to write bytes (True) or str (False). Defaults to False.
90   large_elements_limit: Whether to elide elements attributes above this
91     number of elements. Defaults to None (no limit).
92   enable_debug_info: Whether to print debug/location information. Defaults
93     to False.
94   pretty_debug_info: Whether to format debug information for easier reading
95     by a human (warning: the result is unparseable).
96   print_generic_op_form: Whether to print the generic assembly forms of all
97     ops. Defaults to False.
98   use_local_Scope: Whether to print in a way that is more optimized for
99     multi-threaded access but may not be consistent with how the overall
100     module prints.
101   assume_verified: By default, if not printing generic form, the verifier
102     will be run and if it fails, generic form will be printed with a comment
103     about failed verification. While a reasonable default for interactive use,
104     for systematic use, it is often better for the caller to verify explicitly
105     and report failures in a more robust fashion. Set this to True if doing this
106     in order to avoid running a redundant verification. If the IR is actually
107     invalid, behavior is undefined.
108 )";
109 
110 static const char kOperationGetAsmDocstring[] =
111     R"(Gets the assembly form of the operation with all options available.
112 
113 Args:
114   binary: Whether to return a bytes (True) or str (False) object. Defaults to
115     False.
116   ... others ...: See the print() method for common keyword arguments for
117     configuring the printout.
118 Returns:
119   Either a bytes or str object, depending on the setting of the 'binary'
120   argument.
121 )";
122 
123 static const char kOperationStrDunderDocstring[] =
124     R"(Gets the assembly form of the operation with default options.
125 
126 If more advanced control over the assembly formatting or I/O options is needed,
127 use the dedicated print or get_asm method, which supports keyword arguments to
128 customize behavior.
129 )";
130 
131 static const char kDumpDocstring[] =
132     R"(Dumps a debug representation of the object to stderr.)";
133 
134 static const char kAppendBlockDocstring[] =
135     R"(Appends a new block, with argument types as positional args.
136 
137 Returns:
138   The created block.
139 )";
140 
141 static const char kValueDunderStrDocstring[] =
142     R"(Returns the string form of the value.
143 
144 If the value is a block argument, this is the assembly form of its type and the
145 position in the argument list. If the value is an operation result, this is
146 equivalent to printing the operation that produced it.
147 )";
148 
149 //------------------------------------------------------------------------------
150 // Utilities.
151 //------------------------------------------------------------------------------
152 
153 /// Helper for creating an @classmethod.
154 template <class Func, typename... Args>
155 py::object classmethod(Func f, Args... args) {
156   py::object cf = py::cpp_function(f, args...);
157   return py::reinterpret_borrow<py::object>((PyClassMethod_New(cf.ptr())));
158 }
159 
160 static py::object
161 createCustomDialectWrapper(const std::string &dialectNamespace,
162                            py::object dialectDescriptor) {
163   auto dialectClass = PyGlobals::get().lookupDialectClass(dialectNamespace);
164   if (!dialectClass) {
165     // Use the base class.
166     return py::cast(PyDialect(std::move(dialectDescriptor)));
167   }
168 
169   // Create the custom implementation.
170   return (*dialectClass)(std::move(dialectDescriptor));
171 }
172 
173 static MlirStringRef toMlirStringRef(const std::string &s) {
174   return mlirStringRefCreate(s.data(), s.size());
175 }
176 
177 /// Wrapper for the global LLVM debugging flag.
178 struct PyGlobalDebugFlag {
179   static void set(py::object &o, bool enable) { mlirEnableGlobalDebug(enable); }
180 
181   static bool get(const py::object &) { return mlirIsGlobalDebugEnabled(); }
182 
183   static void bind(py::module &m) {
184     // Debug flags.
185     py::class_<PyGlobalDebugFlag>(m, "_GlobalDebug", py::module_local())
186         .def_property_static("flag", &PyGlobalDebugFlag::get,
187                              &PyGlobalDebugFlag::set, "LLVM-wide debug flag");
188   }
189 };
190 
191 //------------------------------------------------------------------------------
192 // Collections.
193 //------------------------------------------------------------------------------
194 
195 namespace {
196 
197 class PyRegionIterator {
198 public:
199   PyRegionIterator(PyOperationRef operation)
200       : operation(std::move(operation)) {}
201 
202   PyRegionIterator &dunderIter() { return *this; }
203 
204   PyRegion dunderNext() {
205     operation->checkValid();
206     if (nextIndex >= mlirOperationGetNumRegions(operation->get())) {
207       throw py::stop_iteration();
208     }
209     MlirRegion region = mlirOperationGetRegion(operation->get(), nextIndex++);
210     return PyRegion(operation, region);
211   }
212 
213   static void bind(py::module &m) {
214     py::class_<PyRegionIterator>(m, "RegionIterator", py::module_local())
215         .def("__iter__", &PyRegionIterator::dunderIter)
216         .def("__next__", &PyRegionIterator::dunderNext);
217   }
218 
219 private:
220   PyOperationRef operation;
221   int nextIndex = 0;
222 };
223 
224 /// Regions of an op are fixed length and indexed numerically so are represented
225 /// with a sequence-like container.
226 class PyRegionList {
227 public:
228   PyRegionList(PyOperationRef operation) : operation(std::move(operation)) {}
229 
230   intptr_t dunderLen() {
231     operation->checkValid();
232     return mlirOperationGetNumRegions(operation->get());
233   }
234 
235   PyRegion dunderGetItem(intptr_t index) {
236     // dunderLen checks validity.
237     if (index < 0 || index >= dunderLen()) {
238       throw SetPyError(PyExc_IndexError,
239                        "attempt to access out of bounds region");
240     }
241     MlirRegion region = mlirOperationGetRegion(operation->get(), index);
242     return PyRegion(operation, region);
243   }
244 
245   static void bind(py::module &m) {
246     py::class_<PyRegionList>(m, "RegionSequence", py::module_local())
247         .def("__len__", &PyRegionList::dunderLen)
248         .def("__getitem__", &PyRegionList::dunderGetItem);
249   }
250 
251 private:
252   PyOperationRef operation;
253 };
254 
255 class PyBlockIterator {
256 public:
257   PyBlockIterator(PyOperationRef operation, MlirBlock next)
258       : operation(std::move(operation)), next(next) {}
259 
260   PyBlockIterator &dunderIter() { return *this; }
261 
262   PyBlock dunderNext() {
263     operation->checkValid();
264     if (mlirBlockIsNull(next)) {
265       throw py::stop_iteration();
266     }
267 
268     PyBlock returnBlock(operation, next);
269     next = mlirBlockGetNextInRegion(next);
270     return returnBlock;
271   }
272 
273   static void bind(py::module &m) {
274     py::class_<PyBlockIterator>(m, "BlockIterator", py::module_local())
275         .def("__iter__", &PyBlockIterator::dunderIter)
276         .def("__next__", &PyBlockIterator::dunderNext);
277   }
278 
279 private:
280   PyOperationRef operation;
281   MlirBlock next;
282 };
283 
284 /// Blocks are exposed by the C-API as a forward-only linked list. In Python,
285 /// we present them as a more full-featured list-like container but optimize
286 /// it for forward iteration. Blocks are always owned by a region.
287 class PyBlockList {
288 public:
289   PyBlockList(PyOperationRef operation, MlirRegion region)
290       : operation(std::move(operation)), region(region) {}
291 
292   PyBlockIterator dunderIter() {
293     operation->checkValid();
294     return PyBlockIterator(operation, mlirRegionGetFirstBlock(region));
295   }
296 
297   intptr_t dunderLen() {
298     operation->checkValid();
299     intptr_t count = 0;
300     MlirBlock block = mlirRegionGetFirstBlock(region);
301     while (!mlirBlockIsNull(block)) {
302       count += 1;
303       block = mlirBlockGetNextInRegion(block);
304     }
305     return count;
306   }
307 
308   PyBlock dunderGetItem(intptr_t index) {
309     operation->checkValid();
310     if (index < 0) {
311       throw SetPyError(PyExc_IndexError,
312                        "attempt to access out of bounds block");
313     }
314     MlirBlock block = mlirRegionGetFirstBlock(region);
315     while (!mlirBlockIsNull(block)) {
316       if (index == 0) {
317         return PyBlock(operation, block);
318       }
319       block = mlirBlockGetNextInRegion(block);
320       index -= 1;
321     }
322     throw SetPyError(PyExc_IndexError, "attempt to access out of bounds block");
323   }
324 
325   PyBlock appendBlock(const py::args &pyArgTypes) {
326     operation->checkValid();
327     llvm::SmallVector<MlirType, 4> argTypes;
328     argTypes.reserve(pyArgTypes.size());
329     for (auto &pyArg : pyArgTypes) {
330       argTypes.push_back(pyArg.cast<PyType &>());
331     }
332 
333     MlirBlock block = mlirBlockCreate(argTypes.size(), argTypes.data());
334     mlirRegionAppendOwnedBlock(region, block);
335     return PyBlock(operation, block);
336   }
337 
338   static void bind(py::module &m) {
339     py::class_<PyBlockList>(m, "BlockList", py::module_local())
340         .def("__getitem__", &PyBlockList::dunderGetItem)
341         .def("__iter__", &PyBlockList::dunderIter)
342         .def("__len__", &PyBlockList::dunderLen)
343         .def("append", &PyBlockList::appendBlock, kAppendBlockDocstring);
344   }
345 
346 private:
347   PyOperationRef operation;
348   MlirRegion region;
349 };
350 
351 class PyOperationIterator {
352 public:
353   PyOperationIterator(PyOperationRef parentOperation, MlirOperation next)
354       : parentOperation(std::move(parentOperation)), next(next) {}
355 
356   PyOperationIterator &dunderIter() { return *this; }
357 
358   py::object dunderNext() {
359     parentOperation->checkValid();
360     if (mlirOperationIsNull(next)) {
361       throw py::stop_iteration();
362     }
363 
364     PyOperationRef returnOperation =
365         PyOperation::forOperation(parentOperation->getContext(), next);
366     next = mlirOperationGetNextInBlock(next);
367     return returnOperation->createOpView();
368   }
369 
370   static void bind(py::module &m) {
371     py::class_<PyOperationIterator>(m, "OperationIterator", py::module_local())
372         .def("__iter__", &PyOperationIterator::dunderIter)
373         .def("__next__", &PyOperationIterator::dunderNext);
374   }
375 
376 private:
377   PyOperationRef parentOperation;
378   MlirOperation next;
379 };
380 
381 /// Operations are exposed by the C-API as a forward-only linked list. In
382 /// Python, we present them as a more full-featured list-like container but
383 /// optimize it for forward iteration. Iterable operations are always owned
384 /// by a block.
385 class PyOperationList {
386 public:
387   PyOperationList(PyOperationRef parentOperation, MlirBlock block)
388       : parentOperation(std::move(parentOperation)), block(block) {}
389 
390   PyOperationIterator dunderIter() {
391     parentOperation->checkValid();
392     return PyOperationIterator(parentOperation,
393                                mlirBlockGetFirstOperation(block));
394   }
395 
396   intptr_t dunderLen() {
397     parentOperation->checkValid();
398     intptr_t count = 0;
399     MlirOperation childOp = mlirBlockGetFirstOperation(block);
400     while (!mlirOperationIsNull(childOp)) {
401       count += 1;
402       childOp = mlirOperationGetNextInBlock(childOp);
403     }
404     return count;
405   }
406 
407   py::object dunderGetItem(intptr_t index) {
408     parentOperation->checkValid();
409     if (index < 0) {
410       throw SetPyError(PyExc_IndexError,
411                        "attempt to access out of bounds operation");
412     }
413     MlirOperation childOp = mlirBlockGetFirstOperation(block);
414     while (!mlirOperationIsNull(childOp)) {
415       if (index == 0) {
416         return PyOperation::forOperation(parentOperation->getContext(), childOp)
417             ->createOpView();
418       }
419       childOp = mlirOperationGetNextInBlock(childOp);
420       index -= 1;
421     }
422     throw SetPyError(PyExc_IndexError,
423                      "attempt to access out of bounds operation");
424   }
425 
426   static void bind(py::module &m) {
427     py::class_<PyOperationList>(m, "OperationList", py::module_local())
428         .def("__getitem__", &PyOperationList::dunderGetItem)
429         .def("__iter__", &PyOperationList::dunderIter)
430         .def("__len__", &PyOperationList::dunderLen);
431   }
432 
433 private:
434   PyOperationRef parentOperation;
435   MlirBlock block;
436 };
437 
438 } // namespace
439 
440 //------------------------------------------------------------------------------
441 // PyMlirContext
442 //------------------------------------------------------------------------------
443 
444 PyMlirContext::PyMlirContext(MlirContext context) : context(context) {
445   py::gil_scoped_acquire acquire;
446   auto &liveContexts = getLiveContexts();
447   liveContexts[context.ptr] = this;
448 }
449 
450 PyMlirContext::~PyMlirContext() {
451   // Note that the only public way to construct an instance is via the
452   // forContext method, which always puts the associated handle into
453   // liveContexts.
454   py::gil_scoped_acquire acquire;
455   getLiveContexts().erase(context.ptr);
456   mlirContextDestroy(context);
457 }
458 
459 py::object PyMlirContext::getCapsule() {
460   return py::reinterpret_steal<py::object>(mlirPythonContextToCapsule(get()));
461 }
462 
463 py::object PyMlirContext::createFromCapsule(py::object capsule) {
464   MlirContext rawContext = mlirPythonCapsuleToContext(capsule.ptr());
465   if (mlirContextIsNull(rawContext))
466     throw py::error_already_set();
467   return forContext(rawContext).releaseObject();
468 }
469 
470 PyMlirContext *PyMlirContext::createNewContextForInit() {
471   MlirContext context = mlirContextCreate();
472   mlirRegisterAllDialects(context);
473   return new PyMlirContext(context);
474 }
475 
476 PyMlirContextRef PyMlirContext::forContext(MlirContext context) {
477   py::gil_scoped_acquire acquire;
478   auto &liveContexts = getLiveContexts();
479   auto it = liveContexts.find(context.ptr);
480   if (it == liveContexts.end()) {
481     // Create.
482     PyMlirContext *unownedContextWrapper = new PyMlirContext(context);
483     py::object pyRef = py::cast(unownedContextWrapper);
484     assert(pyRef && "cast to py::object failed");
485     liveContexts[context.ptr] = unownedContextWrapper;
486     return PyMlirContextRef(unownedContextWrapper, std::move(pyRef));
487   }
488   // Use existing.
489   py::object pyRef = py::cast(it->second);
490   return PyMlirContextRef(it->second, std::move(pyRef));
491 }
492 
493 PyMlirContext::LiveContextMap &PyMlirContext::getLiveContexts() {
494   static LiveContextMap liveContexts;
495   return liveContexts;
496 }
497 
498 size_t PyMlirContext::getLiveCount() { return getLiveContexts().size(); }
499 
500 size_t PyMlirContext::getLiveOperationCount() { return liveOperations.size(); }
501 
502 size_t PyMlirContext::getLiveModuleCount() { return liveModules.size(); }
503 
504 pybind11::object PyMlirContext::contextEnter() {
505   return PyThreadContextEntry::pushContext(*this);
506 }
507 
508 void PyMlirContext::contextExit(const pybind11::object &excType,
509                                 const pybind11::object &excVal,
510                                 const pybind11::object &excTb) {
511   PyThreadContextEntry::popContext(*this);
512 }
513 
514 PyMlirContext &DefaultingPyMlirContext::resolve() {
515   PyMlirContext *context = PyThreadContextEntry::getDefaultContext();
516   if (!context) {
517     throw SetPyError(
518         PyExc_RuntimeError,
519         "An MLIR function requires a Context but none was provided in the call "
520         "or from the surrounding environment. Either pass to the function with "
521         "a 'context=' argument or establish a default using 'with Context():'");
522   }
523   return *context;
524 }
525 
526 //------------------------------------------------------------------------------
527 // PyThreadContextEntry management
528 //------------------------------------------------------------------------------
529 
530 std::vector<PyThreadContextEntry> &PyThreadContextEntry::getStack() {
531   static thread_local std::vector<PyThreadContextEntry> stack;
532   return stack;
533 }
534 
535 PyThreadContextEntry *PyThreadContextEntry::getTopOfStack() {
536   auto &stack = getStack();
537   if (stack.empty())
538     return nullptr;
539   return &stack.back();
540 }
541 
542 void PyThreadContextEntry::push(FrameKind frameKind, py::object context,
543                                 py::object insertionPoint,
544                                 py::object location) {
545   auto &stack = getStack();
546   stack.emplace_back(frameKind, std::move(context), std::move(insertionPoint),
547                      std::move(location));
548   // If the new stack has more than one entry and the context of the new top
549   // entry matches the previous, copy the insertionPoint and location from the
550   // previous entry if missing from the new top entry.
551   if (stack.size() > 1) {
552     auto &prev = *(stack.rbegin() + 1);
553     auto &current = stack.back();
554     if (current.context.is(prev.context)) {
555       // Default non-context objects from the previous entry.
556       if (!current.insertionPoint)
557         current.insertionPoint = prev.insertionPoint;
558       if (!current.location)
559         current.location = prev.location;
560     }
561   }
562 }
563 
564 PyMlirContext *PyThreadContextEntry::getContext() {
565   if (!context)
566     return nullptr;
567   return py::cast<PyMlirContext *>(context);
568 }
569 
570 PyInsertionPoint *PyThreadContextEntry::getInsertionPoint() {
571   if (!insertionPoint)
572     return nullptr;
573   return py::cast<PyInsertionPoint *>(insertionPoint);
574 }
575 
576 PyLocation *PyThreadContextEntry::getLocation() {
577   if (!location)
578     return nullptr;
579   return py::cast<PyLocation *>(location);
580 }
581 
582 PyMlirContext *PyThreadContextEntry::getDefaultContext() {
583   auto *tos = getTopOfStack();
584   return tos ? tos->getContext() : nullptr;
585 }
586 
587 PyInsertionPoint *PyThreadContextEntry::getDefaultInsertionPoint() {
588   auto *tos = getTopOfStack();
589   return tos ? tos->getInsertionPoint() : nullptr;
590 }
591 
592 PyLocation *PyThreadContextEntry::getDefaultLocation() {
593   auto *tos = getTopOfStack();
594   return tos ? tos->getLocation() : nullptr;
595 }
596 
597 py::object PyThreadContextEntry::pushContext(PyMlirContext &context) {
598   py::object contextObj = py::cast(context);
599   push(FrameKind::Context, /*context=*/contextObj,
600        /*insertionPoint=*/py::object(),
601        /*location=*/py::object());
602   return contextObj;
603 }
604 
605 void PyThreadContextEntry::popContext(PyMlirContext &context) {
606   auto &stack = getStack();
607   if (stack.empty())
608     throw SetPyError(PyExc_RuntimeError, "Unbalanced Context enter/exit");
609   auto &tos = stack.back();
610   if (tos.frameKind != FrameKind::Context && tos.getContext() != &context)
611     throw SetPyError(PyExc_RuntimeError, "Unbalanced Context enter/exit");
612   stack.pop_back();
613 }
614 
615 py::object
616 PyThreadContextEntry::pushInsertionPoint(PyInsertionPoint &insertionPoint) {
617   py::object contextObj =
618       insertionPoint.getBlock().getParentOperation()->getContext().getObject();
619   py::object insertionPointObj = py::cast(insertionPoint);
620   push(FrameKind::InsertionPoint,
621        /*context=*/contextObj,
622        /*insertionPoint=*/insertionPointObj,
623        /*location=*/py::object());
624   return insertionPointObj;
625 }
626 
627 void PyThreadContextEntry::popInsertionPoint(PyInsertionPoint &insertionPoint) {
628   auto &stack = getStack();
629   if (stack.empty())
630     throw SetPyError(PyExc_RuntimeError,
631                      "Unbalanced InsertionPoint enter/exit");
632   auto &tos = stack.back();
633   if (tos.frameKind != FrameKind::InsertionPoint &&
634       tos.getInsertionPoint() != &insertionPoint)
635     throw SetPyError(PyExc_RuntimeError,
636                      "Unbalanced InsertionPoint enter/exit");
637   stack.pop_back();
638 }
639 
640 py::object PyThreadContextEntry::pushLocation(PyLocation &location) {
641   py::object contextObj = location.getContext().getObject();
642   py::object locationObj = py::cast(location);
643   push(FrameKind::Location, /*context=*/contextObj,
644        /*insertionPoint=*/py::object(),
645        /*location=*/locationObj);
646   return locationObj;
647 }
648 
649 void PyThreadContextEntry::popLocation(PyLocation &location) {
650   auto &stack = getStack();
651   if (stack.empty())
652     throw SetPyError(PyExc_RuntimeError, "Unbalanced Location enter/exit");
653   auto &tos = stack.back();
654   if (tos.frameKind != FrameKind::Location && tos.getLocation() != &location)
655     throw SetPyError(PyExc_RuntimeError, "Unbalanced Location enter/exit");
656   stack.pop_back();
657 }
658 
659 //------------------------------------------------------------------------------
660 // PyDialect, PyDialectDescriptor, PyDialects
661 //------------------------------------------------------------------------------
662 
663 MlirDialect PyDialects::getDialectForKey(const std::string &key,
664                                          bool attrError) {
665   MlirDialect dialect = mlirContextGetOrLoadDialect(getContext()->get(),
666                                                     {key.data(), key.size()});
667   if (mlirDialectIsNull(dialect)) {
668     throw SetPyError(attrError ? PyExc_AttributeError : PyExc_IndexError,
669                      Twine("Dialect '") + key + "' not found");
670   }
671   return dialect;
672 }
673 
674 //------------------------------------------------------------------------------
675 // PyLocation
676 //------------------------------------------------------------------------------
677 
678 py::object PyLocation::getCapsule() {
679   return py::reinterpret_steal<py::object>(mlirPythonLocationToCapsule(*this));
680 }
681 
682 PyLocation PyLocation::createFromCapsule(py::object capsule) {
683   MlirLocation rawLoc = mlirPythonCapsuleToLocation(capsule.ptr());
684   if (mlirLocationIsNull(rawLoc))
685     throw py::error_already_set();
686   return PyLocation(PyMlirContext::forContext(mlirLocationGetContext(rawLoc)),
687                     rawLoc);
688 }
689 
690 py::object PyLocation::contextEnter() {
691   return PyThreadContextEntry::pushLocation(*this);
692 }
693 
694 void PyLocation::contextExit(const pybind11::object &excType,
695                              const pybind11::object &excVal,
696                              const pybind11::object &excTb) {
697   PyThreadContextEntry::popLocation(*this);
698 }
699 
700 PyLocation &DefaultingPyLocation::resolve() {
701   auto *location = PyThreadContextEntry::getDefaultLocation();
702   if (!location) {
703     throw SetPyError(
704         PyExc_RuntimeError,
705         "An MLIR function requires a Location but none was provided in the "
706         "call or from the surrounding environment. Either pass to the function "
707         "with a 'loc=' argument or establish a default using 'with loc:'");
708   }
709   return *location;
710 }
711 
712 //------------------------------------------------------------------------------
713 // PyModule
714 //------------------------------------------------------------------------------
715 
716 PyModule::PyModule(PyMlirContextRef contextRef, MlirModule module)
717     : BaseContextObject(std::move(contextRef)), module(module) {}
718 
719 PyModule::~PyModule() {
720   py::gil_scoped_acquire acquire;
721   auto &liveModules = getContext()->liveModules;
722   assert(liveModules.count(module.ptr) == 1 &&
723          "destroying module not in live map");
724   liveModules.erase(module.ptr);
725   mlirModuleDestroy(module);
726 }
727 
728 PyModuleRef PyModule::forModule(MlirModule module) {
729   MlirContext context = mlirModuleGetContext(module);
730   PyMlirContextRef contextRef = PyMlirContext::forContext(context);
731 
732   py::gil_scoped_acquire acquire;
733   auto &liveModules = contextRef->liveModules;
734   auto it = liveModules.find(module.ptr);
735   if (it == liveModules.end()) {
736     // Create.
737     PyModule *unownedModule = new PyModule(std::move(contextRef), module);
738     // Note that the default return value policy on cast is automatic_reference,
739     // which does not take ownership (delete will not be called).
740     // Just be explicit.
741     py::object pyRef =
742         py::cast(unownedModule, py::return_value_policy::take_ownership);
743     unownedModule->handle = pyRef;
744     liveModules[module.ptr] =
745         std::make_pair(unownedModule->handle, unownedModule);
746     return PyModuleRef(unownedModule, std::move(pyRef));
747   }
748   // Use existing.
749   PyModule *existing = it->second.second;
750   py::object pyRef = py::reinterpret_borrow<py::object>(it->second.first);
751   return PyModuleRef(existing, std::move(pyRef));
752 }
753 
754 py::object PyModule::createFromCapsule(py::object capsule) {
755   MlirModule rawModule = mlirPythonCapsuleToModule(capsule.ptr());
756   if (mlirModuleIsNull(rawModule))
757     throw py::error_already_set();
758   return forModule(rawModule).releaseObject();
759 }
760 
761 py::object PyModule::getCapsule() {
762   return py::reinterpret_steal<py::object>(mlirPythonModuleToCapsule(get()));
763 }
764 
765 //------------------------------------------------------------------------------
766 // PyOperation
767 //------------------------------------------------------------------------------
768 
769 PyOperation::PyOperation(PyMlirContextRef contextRef, MlirOperation operation)
770     : BaseContextObject(std::move(contextRef)), operation(operation) {}
771 
772 PyOperation::~PyOperation() {
773   // If the operation has already been invalidated there is nothing to do.
774   if (!valid)
775     return;
776   auto &liveOperations = getContext()->liveOperations;
777   assert(liveOperations.count(operation.ptr) == 1 &&
778          "destroying operation not in live map");
779   liveOperations.erase(operation.ptr);
780   if (!isAttached()) {
781     mlirOperationDestroy(operation);
782   }
783 }
784 
785 PyOperationRef PyOperation::createInstance(PyMlirContextRef contextRef,
786                                            MlirOperation operation,
787                                            py::object parentKeepAlive) {
788   auto &liveOperations = contextRef->liveOperations;
789   // Create.
790   PyOperation *unownedOperation =
791       new PyOperation(std::move(contextRef), operation);
792   // Note that the default return value policy on cast is automatic_reference,
793   // which does not take ownership (delete will not be called).
794   // Just be explicit.
795   py::object pyRef =
796       py::cast(unownedOperation, py::return_value_policy::take_ownership);
797   unownedOperation->handle = pyRef;
798   if (parentKeepAlive) {
799     unownedOperation->parentKeepAlive = std::move(parentKeepAlive);
800   }
801   liveOperations[operation.ptr] = std::make_pair(pyRef, unownedOperation);
802   return PyOperationRef(unownedOperation, std::move(pyRef));
803 }
804 
805 PyOperationRef PyOperation::forOperation(PyMlirContextRef contextRef,
806                                          MlirOperation operation,
807                                          py::object parentKeepAlive) {
808   auto &liveOperations = contextRef->liveOperations;
809   auto it = liveOperations.find(operation.ptr);
810   if (it == liveOperations.end()) {
811     // Create.
812     return createInstance(std::move(contextRef), operation,
813                           std::move(parentKeepAlive));
814   }
815   // Use existing.
816   PyOperation *existing = it->second.second;
817   py::object pyRef = py::reinterpret_borrow<py::object>(it->second.first);
818   return PyOperationRef(existing, std::move(pyRef));
819 }
820 
821 PyOperationRef PyOperation::createDetached(PyMlirContextRef contextRef,
822                                            MlirOperation operation,
823                                            py::object parentKeepAlive) {
824   auto &liveOperations = contextRef->liveOperations;
825   assert(liveOperations.count(operation.ptr) == 0 &&
826          "cannot create detached operation that already exists");
827   (void)liveOperations;
828 
829   PyOperationRef created = createInstance(std::move(contextRef), operation,
830                                           std::move(parentKeepAlive));
831   created->attached = false;
832   return created;
833 }
834 
835 void PyOperation::checkValid() const {
836   if (!valid) {
837     throw SetPyError(PyExc_RuntimeError, "the operation has been invalidated");
838   }
839 }
840 
841 void PyOperationBase::print(py::object fileObject, bool binary,
842                             llvm::Optional<int64_t> largeElementsLimit,
843                             bool enableDebugInfo, bool prettyDebugInfo,
844                             bool printGenericOpForm, bool useLocalScope,
845                             bool assumeVerified) {
846   PyOperation &operation = getOperation();
847   operation.checkValid();
848   if (fileObject.is_none())
849     fileObject = py::module::import("sys").attr("stdout");
850 
851   if (!assumeVerified && !printGenericOpForm &&
852       !mlirOperationVerify(operation)) {
853     std::string message("// Verification failed, printing generic form\n");
854     if (binary) {
855       fileObject.attr("write")(py::bytes(message));
856     } else {
857       fileObject.attr("write")(py::str(message));
858     }
859     printGenericOpForm = true;
860   }
861 
862   MlirOpPrintingFlags flags = mlirOpPrintingFlagsCreate();
863   if (largeElementsLimit)
864     mlirOpPrintingFlagsElideLargeElementsAttrs(flags, *largeElementsLimit);
865   if (enableDebugInfo)
866     mlirOpPrintingFlagsEnableDebugInfo(flags, /*prettyForm=*/prettyDebugInfo);
867   if (printGenericOpForm)
868     mlirOpPrintingFlagsPrintGenericOpForm(flags);
869 
870   PyFileAccumulator accum(fileObject, binary);
871   py::gil_scoped_release();
872   mlirOperationPrintWithFlags(operation, flags, accum.getCallback(),
873                               accum.getUserData());
874   mlirOpPrintingFlagsDestroy(flags);
875 }
876 
877 py::object PyOperationBase::getAsm(bool binary,
878                                    llvm::Optional<int64_t> largeElementsLimit,
879                                    bool enableDebugInfo, bool prettyDebugInfo,
880                                    bool printGenericOpForm, bool useLocalScope,
881                                    bool assumeVerified) {
882   py::object fileObject;
883   if (binary) {
884     fileObject = py::module::import("io").attr("BytesIO")();
885   } else {
886     fileObject = py::module::import("io").attr("StringIO")();
887   }
888   print(fileObject, /*binary=*/binary,
889         /*largeElementsLimit=*/largeElementsLimit,
890         /*enableDebugInfo=*/enableDebugInfo,
891         /*prettyDebugInfo=*/prettyDebugInfo,
892         /*printGenericOpForm=*/printGenericOpForm,
893         /*useLocalScope=*/useLocalScope,
894         /*assumeVerified=*/assumeVerified);
895 
896   return fileObject.attr("getvalue")();
897 }
898 
899 void PyOperationBase::moveAfter(PyOperationBase &other) {
900   PyOperation &operation = getOperation();
901   PyOperation &otherOp = other.getOperation();
902   operation.checkValid();
903   otherOp.checkValid();
904   mlirOperationMoveAfter(operation, otherOp);
905   operation.parentKeepAlive = otherOp.parentKeepAlive;
906 }
907 
908 void PyOperationBase::moveBefore(PyOperationBase &other) {
909   PyOperation &operation = getOperation();
910   PyOperation &otherOp = other.getOperation();
911   operation.checkValid();
912   otherOp.checkValid();
913   mlirOperationMoveBefore(operation, otherOp);
914   operation.parentKeepAlive = otherOp.parentKeepAlive;
915 }
916 
917 llvm::Optional<PyOperationRef> PyOperation::getParentOperation() {
918   checkValid();
919   if (!isAttached())
920     throw SetPyError(PyExc_ValueError, "Detached operations have no parent");
921   MlirOperation operation = mlirOperationGetParentOperation(get());
922   if (mlirOperationIsNull(operation))
923     return {};
924   return PyOperation::forOperation(getContext(), operation);
925 }
926 
927 PyBlock PyOperation::getBlock() {
928   checkValid();
929   llvm::Optional<PyOperationRef> parentOperation = getParentOperation();
930   MlirBlock block = mlirOperationGetBlock(get());
931   assert(!mlirBlockIsNull(block) && "Attached operation has null parent");
932   assert(parentOperation && "Operation has no parent");
933   return PyBlock{std::move(*parentOperation), block};
934 }
935 
936 py::object PyOperation::getCapsule() {
937   checkValid();
938   return py::reinterpret_steal<py::object>(mlirPythonOperationToCapsule(get()));
939 }
940 
941 py::object PyOperation::createFromCapsule(py::object capsule) {
942   MlirOperation rawOperation = mlirPythonCapsuleToOperation(capsule.ptr());
943   if (mlirOperationIsNull(rawOperation))
944     throw py::error_already_set();
945   MlirContext rawCtxt = mlirOperationGetContext(rawOperation);
946   return forOperation(PyMlirContext::forContext(rawCtxt), rawOperation)
947       .releaseObject();
948 }
949 
950 py::object PyOperation::create(
951     const std::string &name, llvm::Optional<std::vector<PyType *>> results,
952     llvm::Optional<std::vector<PyValue *>> operands,
953     llvm::Optional<py::dict> attributes,
954     llvm::Optional<std::vector<PyBlock *>> successors, int regions,
955     DefaultingPyLocation location, const py::object &maybeIp) {
956   llvm::SmallVector<MlirValue, 4> mlirOperands;
957   llvm::SmallVector<MlirType, 4> mlirResults;
958   llvm::SmallVector<MlirBlock, 4> mlirSuccessors;
959   llvm::SmallVector<std::pair<std::string, MlirAttribute>, 4> mlirAttributes;
960 
961   // General parameter validation.
962   if (regions < 0)
963     throw SetPyError(PyExc_ValueError, "number of regions must be >= 0");
964 
965   // Unpack/validate operands.
966   if (operands) {
967     mlirOperands.reserve(operands->size());
968     for (PyValue *operand : *operands) {
969       if (!operand)
970         throw SetPyError(PyExc_ValueError, "operand value cannot be None");
971       mlirOperands.push_back(operand->get());
972     }
973   }
974 
975   // Unpack/validate results.
976   if (results) {
977     mlirResults.reserve(results->size());
978     for (PyType *result : *results) {
979       // TODO: Verify result type originate from the same context.
980       if (!result)
981         throw SetPyError(PyExc_ValueError, "result type cannot be None");
982       mlirResults.push_back(*result);
983     }
984   }
985   // Unpack/validate attributes.
986   if (attributes) {
987     mlirAttributes.reserve(attributes->size());
988     for (auto &it : *attributes) {
989       std::string key;
990       try {
991         key = it.first.cast<std::string>();
992       } catch (py::cast_error &err) {
993         std::string msg = "Invalid attribute key (not a string) when "
994                           "attempting to create the operation \"" +
995                           name + "\" (" + err.what() + ")";
996         throw py::cast_error(msg);
997       }
998       try {
999         auto &attribute = it.second.cast<PyAttribute &>();
1000         // TODO: Verify attribute originates from the same context.
1001         mlirAttributes.emplace_back(std::move(key), attribute);
1002       } catch (py::reference_cast_error &) {
1003         // This exception seems thrown when the value is "None".
1004         std::string msg =
1005             "Found an invalid (`None`?) attribute value for the key \"" + key +
1006             "\" when attempting to create the operation \"" + name + "\"";
1007         throw py::cast_error(msg);
1008       } catch (py::cast_error &err) {
1009         std::string msg = "Invalid attribute value for the key \"" + key +
1010                           "\" when attempting to create the operation \"" +
1011                           name + "\" (" + err.what() + ")";
1012         throw py::cast_error(msg);
1013       }
1014     }
1015   }
1016   // Unpack/validate successors.
1017   if (successors) {
1018     mlirSuccessors.reserve(successors->size());
1019     for (auto *successor : *successors) {
1020       // TODO: Verify successor originate from the same context.
1021       if (!successor)
1022         throw SetPyError(PyExc_ValueError, "successor block cannot be None");
1023       mlirSuccessors.push_back(successor->get());
1024     }
1025   }
1026 
1027   // Apply unpacked/validated to the operation state. Beyond this
1028   // point, exceptions cannot be thrown or else the state will leak.
1029   MlirOperationState state =
1030       mlirOperationStateGet(toMlirStringRef(name), location);
1031   if (!mlirOperands.empty())
1032     mlirOperationStateAddOperands(&state, mlirOperands.size(),
1033                                   mlirOperands.data());
1034   if (!mlirResults.empty())
1035     mlirOperationStateAddResults(&state, mlirResults.size(),
1036                                  mlirResults.data());
1037   if (!mlirAttributes.empty()) {
1038     // Note that the attribute names directly reference bytes in
1039     // mlirAttributes, so that vector must not be changed from here
1040     // on.
1041     llvm::SmallVector<MlirNamedAttribute, 4> mlirNamedAttributes;
1042     mlirNamedAttributes.reserve(mlirAttributes.size());
1043     for (auto &it : mlirAttributes)
1044       mlirNamedAttributes.push_back(mlirNamedAttributeGet(
1045           mlirIdentifierGet(mlirAttributeGetContext(it.second),
1046                             toMlirStringRef(it.first)),
1047           it.second));
1048     mlirOperationStateAddAttributes(&state, mlirNamedAttributes.size(),
1049                                     mlirNamedAttributes.data());
1050   }
1051   if (!mlirSuccessors.empty())
1052     mlirOperationStateAddSuccessors(&state, mlirSuccessors.size(),
1053                                     mlirSuccessors.data());
1054   if (regions) {
1055     llvm::SmallVector<MlirRegion, 4> mlirRegions;
1056     mlirRegions.resize(regions);
1057     for (int i = 0; i < regions; ++i)
1058       mlirRegions[i] = mlirRegionCreate();
1059     mlirOperationStateAddOwnedRegions(&state, mlirRegions.size(),
1060                                       mlirRegions.data());
1061   }
1062 
1063   // Construct the operation.
1064   MlirOperation operation = mlirOperationCreate(&state);
1065   PyOperationRef created =
1066       PyOperation::createDetached(location->getContext(), operation);
1067 
1068   // InsertPoint active?
1069   if (!maybeIp.is(py::cast(false))) {
1070     PyInsertionPoint *ip;
1071     if (maybeIp.is_none()) {
1072       ip = PyThreadContextEntry::getDefaultInsertionPoint();
1073     } else {
1074       ip = py::cast<PyInsertionPoint *>(maybeIp);
1075     }
1076     if (ip)
1077       ip->insert(*created.get());
1078   }
1079 
1080   return created->createOpView();
1081 }
1082 
1083 py::object PyOperation::createOpView() {
1084   checkValid();
1085   MlirIdentifier ident = mlirOperationGetName(get());
1086   MlirStringRef identStr = mlirIdentifierStr(ident);
1087   auto opViewClass = PyGlobals::get().lookupRawOpViewClass(
1088       StringRef(identStr.data, identStr.length));
1089   if (opViewClass)
1090     return (*opViewClass)(getRef().getObject());
1091   return py::cast(PyOpView(getRef().getObject()));
1092 }
1093 
1094 void PyOperation::erase() {
1095   checkValid();
1096   // TODO: Fix memory hazards when erasing a tree of operations for which a deep
1097   // Python reference to a child operation is live. All children should also
1098   // have their `valid` bit set to false.
1099   auto &liveOperations = getContext()->liveOperations;
1100   if (liveOperations.count(operation.ptr))
1101     liveOperations.erase(operation.ptr);
1102   mlirOperationDestroy(operation);
1103   valid = false;
1104 }
1105 
1106 //------------------------------------------------------------------------------
1107 // PyOpView
1108 //------------------------------------------------------------------------------
1109 
1110 py::object PyOpView::buildGeneric(
1111     const py::object &cls, py::list resultTypeList, py::list operandList,
1112     llvm::Optional<py::dict> attributes,
1113     llvm::Optional<std::vector<PyBlock *>> successors,
1114     llvm::Optional<int> regions, DefaultingPyLocation location,
1115     const py::object &maybeIp) {
1116   PyMlirContextRef context = location->getContext();
1117   // Class level operation construction metadata.
1118   std::string name = py::cast<std::string>(cls.attr("OPERATION_NAME"));
1119   // Operand and result segment specs are either none, which does no
1120   // variadic unpacking, or a list of ints with segment sizes, where each
1121   // element is either a positive number (typically 1 for a scalar) or -1 to
1122   // indicate that it is derived from the length of the same-indexed operand
1123   // or result (implying that it is a list at that position).
1124   py::object operandSegmentSpecObj = cls.attr("_ODS_OPERAND_SEGMENTS");
1125   py::object resultSegmentSpecObj = cls.attr("_ODS_RESULT_SEGMENTS");
1126 
1127   std::vector<uint32_t> operandSegmentLengths;
1128   std::vector<uint32_t> resultSegmentLengths;
1129 
1130   // Validate/determine region count.
1131   auto opRegionSpec = py::cast<std::tuple<int, bool>>(cls.attr("_ODS_REGIONS"));
1132   int opMinRegionCount = std::get<0>(opRegionSpec);
1133   bool opHasNoVariadicRegions = std::get<1>(opRegionSpec);
1134   if (!regions) {
1135     regions = opMinRegionCount;
1136   }
1137   if (*regions < opMinRegionCount) {
1138     throw py::value_error(
1139         (llvm::Twine("Operation \"") + name + "\" requires a minimum of " +
1140          llvm::Twine(opMinRegionCount) +
1141          " regions but was built with regions=" + llvm::Twine(*regions))
1142             .str());
1143   }
1144   if (opHasNoVariadicRegions && *regions > opMinRegionCount) {
1145     throw py::value_error(
1146         (llvm::Twine("Operation \"") + name + "\" requires a maximum of " +
1147          llvm::Twine(opMinRegionCount) +
1148          " regions but was built with regions=" + llvm::Twine(*regions))
1149             .str());
1150   }
1151 
1152   // Unpack results.
1153   std::vector<PyType *> resultTypes;
1154   resultTypes.reserve(resultTypeList.size());
1155   if (resultSegmentSpecObj.is_none()) {
1156     // Non-variadic result unpacking.
1157     for (const auto &it : llvm::enumerate(resultTypeList)) {
1158       try {
1159         resultTypes.push_back(py::cast<PyType *>(it.value()));
1160         if (!resultTypes.back())
1161           throw py::cast_error();
1162       } catch (py::cast_error &err) {
1163         throw py::value_error((llvm::Twine("Result ") +
1164                                llvm::Twine(it.index()) + " of operation \"" +
1165                                name + "\" must be a Type (" + err.what() + ")")
1166                                   .str());
1167       }
1168     }
1169   } else {
1170     // Sized result unpacking.
1171     auto resultSegmentSpec = py::cast<std::vector<int>>(resultSegmentSpecObj);
1172     if (resultSegmentSpec.size() != resultTypeList.size()) {
1173       throw py::value_error((llvm::Twine("Operation \"") + name +
1174                              "\" requires " +
1175                              llvm::Twine(resultSegmentSpec.size()) +
1176                              " result segments but was provided " +
1177                              llvm::Twine(resultTypeList.size()))
1178                                 .str());
1179     }
1180     resultSegmentLengths.reserve(resultTypeList.size());
1181     for (const auto &it :
1182          llvm::enumerate(llvm::zip(resultTypeList, resultSegmentSpec))) {
1183       int segmentSpec = std::get<1>(it.value());
1184       if (segmentSpec == 1 || segmentSpec == 0) {
1185         // Unpack unary element.
1186         try {
1187           auto *resultType = py::cast<PyType *>(std::get<0>(it.value()));
1188           if (resultType) {
1189             resultTypes.push_back(resultType);
1190             resultSegmentLengths.push_back(1);
1191           } else if (segmentSpec == 0) {
1192             // Allowed to be optional.
1193             resultSegmentLengths.push_back(0);
1194           } else {
1195             throw py::cast_error("was None and result is not optional");
1196           }
1197         } catch (py::cast_error &err) {
1198           throw py::value_error((llvm::Twine("Result ") +
1199                                  llvm::Twine(it.index()) + " of operation \"" +
1200                                  name + "\" must be a Type (" + err.what() +
1201                                  ")")
1202                                     .str());
1203         }
1204       } else if (segmentSpec == -1) {
1205         // Unpack sequence by appending.
1206         try {
1207           if (std::get<0>(it.value()).is_none()) {
1208             // Treat it as an empty list.
1209             resultSegmentLengths.push_back(0);
1210           } else {
1211             // Unpack the list.
1212             auto segment = py::cast<py::sequence>(std::get<0>(it.value()));
1213             for (py::object segmentItem : segment) {
1214               resultTypes.push_back(py::cast<PyType *>(segmentItem));
1215               if (!resultTypes.back()) {
1216                 throw py::cast_error("contained a None item");
1217               }
1218             }
1219             resultSegmentLengths.push_back(segment.size());
1220           }
1221         } catch (std::exception &err) {
1222           // NOTE: Sloppy to be using a catch-all here, but there are at least
1223           // three different unrelated exceptions that can be thrown in the
1224           // above "casts". Just keep the scope above small and catch them all.
1225           throw py::value_error((llvm::Twine("Result ") +
1226                                  llvm::Twine(it.index()) + " of operation \"" +
1227                                  name + "\" must be a Sequence of Types (" +
1228                                  err.what() + ")")
1229                                     .str());
1230         }
1231       } else {
1232         throw py::value_error("Unexpected segment spec");
1233       }
1234     }
1235   }
1236 
1237   // Unpack operands.
1238   std::vector<PyValue *> operands;
1239   operands.reserve(operands.size());
1240   if (operandSegmentSpecObj.is_none()) {
1241     // Non-sized operand unpacking.
1242     for (const auto &it : llvm::enumerate(operandList)) {
1243       try {
1244         operands.push_back(py::cast<PyValue *>(it.value()));
1245         if (!operands.back())
1246           throw py::cast_error();
1247       } catch (py::cast_error &err) {
1248         throw py::value_error((llvm::Twine("Operand ") +
1249                                llvm::Twine(it.index()) + " of operation \"" +
1250                                name + "\" must be a Value (" + err.what() + ")")
1251                                   .str());
1252       }
1253     }
1254   } else {
1255     // Sized operand unpacking.
1256     auto operandSegmentSpec = py::cast<std::vector<int>>(operandSegmentSpecObj);
1257     if (operandSegmentSpec.size() != operandList.size()) {
1258       throw py::value_error((llvm::Twine("Operation \"") + name +
1259                              "\" requires " +
1260                              llvm::Twine(operandSegmentSpec.size()) +
1261                              "operand segments but was provided " +
1262                              llvm::Twine(operandList.size()))
1263                                 .str());
1264     }
1265     operandSegmentLengths.reserve(operandList.size());
1266     for (const auto &it :
1267          llvm::enumerate(llvm::zip(operandList, operandSegmentSpec))) {
1268       int segmentSpec = std::get<1>(it.value());
1269       if (segmentSpec == 1 || segmentSpec == 0) {
1270         // Unpack unary element.
1271         try {
1272           auto *operandValue = py::cast<PyValue *>(std::get<0>(it.value()));
1273           if (operandValue) {
1274             operands.push_back(operandValue);
1275             operandSegmentLengths.push_back(1);
1276           } else if (segmentSpec == 0) {
1277             // Allowed to be optional.
1278             operandSegmentLengths.push_back(0);
1279           } else {
1280             throw py::cast_error("was None and operand is not optional");
1281           }
1282         } catch (py::cast_error &err) {
1283           throw py::value_error((llvm::Twine("Operand ") +
1284                                  llvm::Twine(it.index()) + " of operation \"" +
1285                                  name + "\" must be a Value (" + err.what() +
1286                                  ")")
1287                                     .str());
1288         }
1289       } else if (segmentSpec == -1) {
1290         // Unpack sequence by appending.
1291         try {
1292           if (std::get<0>(it.value()).is_none()) {
1293             // Treat it as an empty list.
1294             operandSegmentLengths.push_back(0);
1295           } else {
1296             // Unpack the list.
1297             auto segment = py::cast<py::sequence>(std::get<0>(it.value()));
1298             for (py::object segmentItem : segment) {
1299               operands.push_back(py::cast<PyValue *>(segmentItem));
1300               if (!operands.back()) {
1301                 throw py::cast_error("contained a None item");
1302               }
1303             }
1304             operandSegmentLengths.push_back(segment.size());
1305           }
1306         } catch (std::exception &err) {
1307           // NOTE: Sloppy to be using a catch-all here, but there are at least
1308           // three different unrelated exceptions that can be thrown in the
1309           // above "casts". Just keep the scope above small and catch them all.
1310           throw py::value_error((llvm::Twine("Operand ") +
1311                                  llvm::Twine(it.index()) + " of operation \"" +
1312                                  name + "\" must be a Sequence of Values (" +
1313                                  err.what() + ")")
1314                                     .str());
1315         }
1316       } else {
1317         throw py::value_error("Unexpected segment spec");
1318       }
1319     }
1320   }
1321 
1322   // Merge operand/result segment lengths into attributes if needed.
1323   if (!operandSegmentLengths.empty() || !resultSegmentLengths.empty()) {
1324     // Dup.
1325     if (attributes) {
1326       attributes = py::dict(*attributes);
1327     } else {
1328       attributes = py::dict();
1329     }
1330     if (attributes->contains("result_segment_sizes") ||
1331         attributes->contains("operand_segment_sizes")) {
1332       throw py::value_error("Manually setting a 'result_segment_sizes' or "
1333                             "'operand_segment_sizes' attribute is unsupported. "
1334                             "Use Operation.create for such low-level access.");
1335     }
1336 
1337     // Add result_segment_sizes attribute.
1338     if (!resultSegmentLengths.empty()) {
1339       int64_t size = resultSegmentLengths.size();
1340       MlirAttribute segmentLengthAttr = mlirDenseElementsAttrUInt32Get(
1341           mlirVectorTypeGet(1, &size, mlirIntegerTypeGet(context->get(), 32)),
1342           resultSegmentLengths.size(), resultSegmentLengths.data());
1343       (*attributes)["result_segment_sizes"] =
1344           PyAttribute(context, segmentLengthAttr);
1345     }
1346 
1347     // Add operand_segment_sizes attribute.
1348     if (!operandSegmentLengths.empty()) {
1349       int64_t size = operandSegmentLengths.size();
1350       MlirAttribute segmentLengthAttr = mlirDenseElementsAttrUInt32Get(
1351           mlirVectorTypeGet(1, &size, mlirIntegerTypeGet(context->get(), 32)),
1352           operandSegmentLengths.size(), operandSegmentLengths.data());
1353       (*attributes)["operand_segment_sizes"] =
1354           PyAttribute(context, segmentLengthAttr);
1355     }
1356   }
1357 
1358   // Delegate to create.
1359   return PyOperation::create(name,
1360                              /*results=*/std::move(resultTypes),
1361                              /*operands=*/std::move(operands),
1362                              /*attributes=*/std::move(attributes),
1363                              /*successors=*/std::move(successors),
1364                              /*regions=*/*regions, location, maybeIp);
1365 }
1366 
1367 PyOpView::PyOpView(const py::object &operationObject)
1368     // Casting through the PyOperationBase base-class and then back to the
1369     // Operation lets us accept any PyOperationBase subclass.
1370     : operation(py::cast<PyOperationBase &>(operationObject).getOperation()),
1371       operationObject(operation.getRef().getObject()) {}
1372 
1373 py::object PyOpView::createRawSubclass(const py::object &userClass) {
1374   // This is... a little gross. The typical pattern is to have a pure python
1375   // class that extends OpView like:
1376   //   class AddFOp(_cext.ir.OpView):
1377   //     def __init__(self, loc, lhs, rhs):
1378   //       operation = loc.context.create_operation(
1379   //           "addf", lhs, rhs, results=[lhs.type])
1380   //       super().__init__(operation)
1381   //
1382   // I.e. The goal of the user facing type is to provide a nice constructor
1383   // that has complete freedom for the op under construction. This is at odds
1384   // with our other desire to sometimes create this object by just passing an
1385   // operation (to initialize the base class). We could do *arg and **kwargs
1386   // munging to try to make it work, but instead, we synthesize a new class
1387   // on the fly which extends this user class (AddFOp in this example) and
1388   // *give it* the base class's __init__ method, thus bypassing the
1389   // intermediate subclass's __init__ method entirely. While slightly,
1390   // underhanded, this is safe/legal because the type hierarchy has not changed
1391   // (we just added a new leaf) and we aren't mucking around with __new__.
1392   // Typically, this new class will be stored on the original as "_Raw" and will
1393   // be used for casts and other things that need a variant of the class that
1394   // is initialized purely from an operation.
1395   py::object parentMetaclass =
1396       py::reinterpret_borrow<py::object>((PyObject *)&PyType_Type);
1397   py::dict attributes;
1398   // TODO: pybind11 2.6 supports a more direct form. Upgrade many years from
1399   // now.
1400   //   auto opViewType = py::type::of<PyOpView>();
1401   auto opViewType = py::detail::get_type_handle(typeid(PyOpView), true);
1402   attributes["__init__"] = opViewType.attr("__init__");
1403   py::str origName = userClass.attr("__name__");
1404   py::str newName = py::str("_") + origName;
1405   return parentMetaclass(newName, py::make_tuple(userClass), attributes);
1406 }
1407 
1408 //------------------------------------------------------------------------------
1409 // PyInsertionPoint.
1410 //------------------------------------------------------------------------------
1411 
1412 PyInsertionPoint::PyInsertionPoint(PyBlock &block) : block(block) {}
1413 
1414 PyInsertionPoint::PyInsertionPoint(PyOperationBase &beforeOperationBase)
1415     : refOperation(beforeOperationBase.getOperation().getRef()),
1416       block((*refOperation)->getBlock()) {}
1417 
1418 void PyInsertionPoint::insert(PyOperationBase &operationBase) {
1419   PyOperation &operation = operationBase.getOperation();
1420   if (operation.isAttached())
1421     throw SetPyError(PyExc_ValueError,
1422                      "Attempt to insert operation that is already attached");
1423   block.getParentOperation()->checkValid();
1424   MlirOperation beforeOp = {nullptr};
1425   if (refOperation) {
1426     // Insert before operation.
1427     (*refOperation)->checkValid();
1428     beforeOp = (*refOperation)->get();
1429   } else {
1430     // Insert at end (before null) is only valid if the block does not
1431     // already end in a known terminator (violating this will cause assertion
1432     // failures later).
1433     if (!mlirOperationIsNull(mlirBlockGetTerminator(block.get()))) {
1434       throw py::index_error("Cannot insert operation at the end of a block "
1435                             "that already has a terminator. Did you mean to "
1436                             "use 'InsertionPoint.at_block_terminator(block)' "
1437                             "versus 'InsertionPoint(block)'?");
1438     }
1439   }
1440   mlirBlockInsertOwnedOperationBefore(block.get(), beforeOp, operation);
1441   operation.setAttached();
1442 }
1443 
1444 PyInsertionPoint PyInsertionPoint::atBlockBegin(PyBlock &block) {
1445   MlirOperation firstOp = mlirBlockGetFirstOperation(block.get());
1446   if (mlirOperationIsNull(firstOp)) {
1447     // Just insert at end.
1448     return PyInsertionPoint(block);
1449   }
1450 
1451   // Insert before first op.
1452   PyOperationRef firstOpRef = PyOperation::forOperation(
1453       block.getParentOperation()->getContext(), firstOp);
1454   return PyInsertionPoint{block, std::move(firstOpRef)};
1455 }
1456 
1457 PyInsertionPoint PyInsertionPoint::atBlockTerminator(PyBlock &block) {
1458   MlirOperation terminator = mlirBlockGetTerminator(block.get());
1459   if (mlirOperationIsNull(terminator))
1460     throw SetPyError(PyExc_ValueError, "Block has no terminator");
1461   PyOperationRef terminatorOpRef = PyOperation::forOperation(
1462       block.getParentOperation()->getContext(), terminator);
1463   return PyInsertionPoint{block, std::move(terminatorOpRef)};
1464 }
1465 
1466 py::object PyInsertionPoint::contextEnter() {
1467   return PyThreadContextEntry::pushInsertionPoint(*this);
1468 }
1469 
1470 void PyInsertionPoint::contextExit(const pybind11::object &excType,
1471                                    const pybind11::object &excVal,
1472                                    const pybind11::object &excTb) {
1473   PyThreadContextEntry::popInsertionPoint(*this);
1474 }
1475 
1476 //------------------------------------------------------------------------------
1477 // PyAttribute.
1478 //------------------------------------------------------------------------------
1479 
1480 bool PyAttribute::operator==(const PyAttribute &other) {
1481   return mlirAttributeEqual(attr, other.attr);
1482 }
1483 
1484 py::object PyAttribute::getCapsule() {
1485   return py::reinterpret_steal<py::object>(mlirPythonAttributeToCapsule(*this));
1486 }
1487 
1488 PyAttribute PyAttribute::createFromCapsule(py::object capsule) {
1489   MlirAttribute rawAttr = mlirPythonCapsuleToAttribute(capsule.ptr());
1490   if (mlirAttributeIsNull(rawAttr))
1491     throw py::error_already_set();
1492   return PyAttribute(
1493       PyMlirContext::forContext(mlirAttributeGetContext(rawAttr)), rawAttr);
1494 }
1495 
1496 //------------------------------------------------------------------------------
1497 // PyNamedAttribute.
1498 //------------------------------------------------------------------------------
1499 
1500 PyNamedAttribute::PyNamedAttribute(MlirAttribute attr, std::string ownedName)
1501     : ownedName(new std::string(std::move(ownedName))) {
1502   namedAttr = mlirNamedAttributeGet(
1503       mlirIdentifierGet(mlirAttributeGetContext(attr),
1504                         toMlirStringRef(*this->ownedName)),
1505       attr);
1506 }
1507 
1508 //------------------------------------------------------------------------------
1509 // PyType.
1510 //------------------------------------------------------------------------------
1511 
1512 bool PyType::operator==(const PyType &other) {
1513   return mlirTypeEqual(type, other.type);
1514 }
1515 
1516 py::object PyType::getCapsule() {
1517   return py::reinterpret_steal<py::object>(mlirPythonTypeToCapsule(*this));
1518 }
1519 
1520 PyType PyType::createFromCapsule(py::object capsule) {
1521   MlirType rawType = mlirPythonCapsuleToType(capsule.ptr());
1522   if (mlirTypeIsNull(rawType))
1523     throw py::error_already_set();
1524   return PyType(PyMlirContext::forContext(mlirTypeGetContext(rawType)),
1525                 rawType);
1526 }
1527 
1528 //------------------------------------------------------------------------------
1529 // PyValue and subclases.
1530 //------------------------------------------------------------------------------
1531 
1532 pybind11::object PyValue::getCapsule() {
1533   return py::reinterpret_steal<py::object>(mlirPythonValueToCapsule(get()));
1534 }
1535 
1536 PyValue PyValue::createFromCapsule(pybind11::object capsule) {
1537   MlirValue value = mlirPythonCapsuleToValue(capsule.ptr());
1538   if (mlirValueIsNull(value))
1539     throw py::error_already_set();
1540   MlirOperation owner;
1541   if (mlirValueIsAOpResult(value))
1542     owner = mlirOpResultGetOwner(value);
1543   if (mlirValueIsABlockArgument(value))
1544     owner = mlirBlockGetParentOperation(mlirBlockArgumentGetOwner(value));
1545   if (mlirOperationIsNull(owner))
1546     throw py::error_already_set();
1547   MlirContext ctx = mlirOperationGetContext(owner);
1548   PyOperationRef ownerRef =
1549       PyOperation::forOperation(PyMlirContext::forContext(ctx), owner);
1550   return PyValue(ownerRef, value);
1551 }
1552 
1553 //------------------------------------------------------------------------------
1554 // PySymbolTable.
1555 //------------------------------------------------------------------------------
1556 
1557 PySymbolTable::PySymbolTable(PyOperationBase &operation)
1558     : operation(operation.getOperation().getRef()) {
1559   symbolTable = mlirSymbolTableCreate(operation.getOperation().get());
1560   if (mlirSymbolTableIsNull(symbolTable)) {
1561     throw py::cast_error("Operation is not a Symbol Table.");
1562   }
1563 }
1564 
1565 py::object PySymbolTable::dunderGetItem(const std::string &name) {
1566   operation->checkValid();
1567   MlirOperation symbol = mlirSymbolTableLookup(
1568       symbolTable, mlirStringRefCreate(name.data(), name.length()));
1569   if (mlirOperationIsNull(symbol))
1570     throw py::key_error("Symbol '" + name + "' not in the symbol table.");
1571 
1572   return PyOperation::forOperation(operation->getContext(), symbol,
1573                                    operation.getObject())
1574       ->createOpView();
1575 }
1576 
1577 void PySymbolTable::erase(PyOperationBase &symbol) {
1578   operation->checkValid();
1579   symbol.getOperation().checkValid();
1580   mlirSymbolTableErase(symbolTable, symbol.getOperation().get());
1581   // The operation is also erased, so we must invalidate it. There may be Python
1582   // references to this operation so we don't want to delete it from the list of
1583   // live operations here.
1584   symbol.getOperation().valid = false;
1585 }
1586 
1587 void PySymbolTable::dunderDel(const std::string &name) {
1588   py::object operation = dunderGetItem(name);
1589   erase(py::cast<PyOperationBase &>(operation));
1590 }
1591 
1592 PyAttribute PySymbolTable::insert(PyOperationBase &symbol) {
1593   operation->checkValid();
1594   symbol.getOperation().checkValid();
1595   MlirAttribute symbolAttr = mlirOperationGetAttributeByName(
1596       symbol.getOperation().get(), mlirSymbolTableGetSymbolAttributeName());
1597   if (mlirAttributeIsNull(symbolAttr))
1598     throw py::value_error("Expected operation to have a symbol name.");
1599   return PyAttribute(
1600       symbol.getOperation().getContext(),
1601       mlirSymbolTableInsert(symbolTable, symbol.getOperation().get()));
1602 }
1603 
1604 PyAttribute PySymbolTable::getSymbolName(PyOperationBase &symbol) {
1605   // Op must already be a symbol.
1606   PyOperation &operation = symbol.getOperation();
1607   operation.checkValid();
1608   MlirStringRef attrName = mlirSymbolTableGetSymbolAttributeName();
1609   MlirAttribute existingNameAttr =
1610       mlirOperationGetAttributeByName(operation.get(), attrName);
1611   if (mlirAttributeIsNull(existingNameAttr))
1612     throw py::value_error("Expected operation to have a symbol name.");
1613   return PyAttribute(symbol.getOperation().getContext(), existingNameAttr);
1614 }
1615 
1616 void PySymbolTable::setSymbolName(PyOperationBase &symbol,
1617                                   const std::string &name) {
1618   // Op must already be a symbol.
1619   PyOperation &operation = symbol.getOperation();
1620   operation.checkValid();
1621   MlirStringRef attrName = mlirSymbolTableGetSymbolAttributeName();
1622   MlirAttribute existingNameAttr =
1623       mlirOperationGetAttributeByName(operation.get(), attrName);
1624   if (mlirAttributeIsNull(existingNameAttr))
1625     throw py::value_error("Expected operation to have a symbol name.");
1626   MlirAttribute newNameAttr =
1627       mlirStringAttrGet(operation.getContext()->get(), toMlirStringRef(name));
1628   mlirOperationSetAttributeByName(operation.get(), attrName, newNameAttr);
1629 }
1630 
1631 PyAttribute PySymbolTable::getVisibility(PyOperationBase &symbol) {
1632   PyOperation &operation = symbol.getOperation();
1633   operation.checkValid();
1634   MlirStringRef attrName = mlirSymbolTableGetVisibilityAttributeName();
1635   MlirAttribute existingVisAttr =
1636       mlirOperationGetAttributeByName(operation.get(), attrName);
1637   if (mlirAttributeIsNull(existingVisAttr))
1638     throw py::value_error("Expected operation to have a symbol visibility.");
1639   return PyAttribute(symbol.getOperation().getContext(), existingVisAttr);
1640 }
1641 
1642 void PySymbolTable::setVisibility(PyOperationBase &symbol,
1643                                   const std::string &visibility) {
1644   if (visibility != "public" && visibility != "private" &&
1645       visibility != "nested")
1646     throw py::value_error(
1647         "Expected visibility to be 'public', 'private' or 'nested'");
1648   PyOperation &operation = symbol.getOperation();
1649   operation.checkValid();
1650   MlirStringRef attrName = mlirSymbolTableGetVisibilityAttributeName();
1651   MlirAttribute existingVisAttr =
1652       mlirOperationGetAttributeByName(operation.get(), attrName);
1653   if (mlirAttributeIsNull(existingVisAttr))
1654     throw py::value_error("Expected operation to have a symbol visibility.");
1655   MlirAttribute newVisAttr = mlirStringAttrGet(operation.getContext()->get(),
1656                                                toMlirStringRef(visibility));
1657   mlirOperationSetAttributeByName(operation.get(), attrName, newVisAttr);
1658 }
1659 
1660 void PySymbolTable::replaceAllSymbolUses(const std::string &oldSymbol,
1661                                          const std::string &newSymbol,
1662                                          PyOperationBase &from) {
1663   PyOperation &fromOperation = from.getOperation();
1664   fromOperation.checkValid();
1665   if (mlirLogicalResultIsFailure(mlirSymbolTableReplaceAllSymbolUses(
1666           toMlirStringRef(oldSymbol), toMlirStringRef(newSymbol),
1667           from.getOperation())))
1668 
1669     throw py::value_error("Symbol rename failed");
1670 }
1671 
1672 void PySymbolTable::walkSymbolTables(PyOperationBase &from,
1673                                      bool allSymUsesVisible,
1674                                      py::object callback) {
1675   PyOperation &fromOperation = from.getOperation();
1676   fromOperation.checkValid();
1677   struct UserData {
1678     PyMlirContextRef context;
1679     py::object callback;
1680     bool gotException;
1681     std::string exceptionWhat;
1682     py::object exceptionType;
1683   };
1684   UserData userData{
1685       fromOperation.getContext(), std::move(callback), false, {}, {}};
1686   mlirSymbolTableWalkSymbolTables(
1687       fromOperation.get(), allSymUsesVisible,
1688       [](MlirOperation foundOp, bool isVisible, void *calleeUserDataVoid) {
1689         UserData *calleeUserData = static_cast<UserData *>(calleeUserDataVoid);
1690         auto pyFoundOp =
1691             PyOperation::forOperation(calleeUserData->context, foundOp);
1692         if (calleeUserData->gotException)
1693           return;
1694         try {
1695           calleeUserData->callback(pyFoundOp.getObject(), isVisible);
1696         } catch (py::error_already_set &e) {
1697           calleeUserData->gotException = true;
1698           calleeUserData->exceptionWhat = e.what();
1699           calleeUserData->exceptionType = e.type();
1700         }
1701       },
1702       static_cast<void *>(&userData));
1703   if (userData.gotException) {
1704     std::string message("Exception raised in callback: ");
1705     message.append(userData.exceptionWhat);
1706     throw std::runtime_error(message);
1707   }
1708 }
1709 
1710 namespace {
1711 /// CRTP base class for Python MLIR values that subclass Value and should be
1712 /// castable from it. The value hierarchy is one level deep and is not supposed
1713 /// to accommodate other levels unless core MLIR changes.
1714 template <typename DerivedTy>
1715 class PyConcreteValue : public PyValue {
1716 public:
1717   // Derived classes must define statics for:
1718   //   IsAFunctionTy isaFunction
1719   //   const char *pyClassName
1720   // and redefine bindDerived.
1721   using ClassTy = py::class_<DerivedTy, PyValue>;
1722   using IsAFunctionTy = bool (*)(MlirValue);
1723 
1724   PyConcreteValue() = default;
1725   PyConcreteValue(PyOperationRef operationRef, MlirValue value)
1726       : PyValue(operationRef, value) {}
1727   PyConcreteValue(PyValue &orig)
1728       : PyConcreteValue(orig.getParentOperation(), castFrom(orig)) {}
1729 
1730   /// Attempts to cast the original value to the derived type and throws on
1731   /// type mismatches.
1732   static MlirValue castFrom(PyValue &orig) {
1733     if (!DerivedTy::isaFunction(orig.get())) {
1734       auto origRepr = py::repr(py::cast(orig)).cast<std::string>();
1735       throw SetPyError(PyExc_ValueError, Twine("Cannot cast value to ") +
1736                                              DerivedTy::pyClassName +
1737                                              " (from " + origRepr + ")");
1738     }
1739     return orig.get();
1740   }
1741 
1742   /// Binds the Python module objects to functions of this class.
1743   static void bind(py::module &m) {
1744     auto cls = ClassTy(m, DerivedTy::pyClassName, py::module_local());
1745     cls.def(py::init<PyValue &>(), py::keep_alive<0, 1>(), py::arg("value"));
1746     cls.def_static(
1747         "isinstance",
1748         [](PyValue &otherValue) -> bool {
1749           return DerivedTy::isaFunction(otherValue);
1750         },
1751         py::arg("other_value"));
1752     DerivedTy::bindDerived(cls);
1753   }
1754 
1755   /// Implemented by derived classes to add methods to the Python subclass.
1756   static void bindDerived(ClassTy &m) {}
1757 };
1758 
1759 /// Python wrapper for MlirBlockArgument.
1760 class PyBlockArgument : public PyConcreteValue<PyBlockArgument> {
1761 public:
1762   static constexpr IsAFunctionTy isaFunction = mlirValueIsABlockArgument;
1763   static constexpr const char *pyClassName = "BlockArgument";
1764   using PyConcreteValue::PyConcreteValue;
1765 
1766   static void bindDerived(ClassTy &c) {
1767     c.def_property_readonly("owner", [](PyBlockArgument &self) {
1768       return PyBlock(self.getParentOperation(),
1769                      mlirBlockArgumentGetOwner(self.get()));
1770     });
1771     c.def_property_readonly("arg_number", [](PyBlockArgument &self) {
1772       return mlirBlockArgumentGetArgNumber(self.get());
1773     });
1774     c.def(
1775         "set_type",
1776         [](PyBlockArgument &self, PyType type) {
1777           return mlirBlockArgumentSetType(self.get(), type);
1778         },
1779         py::arg("type"));
1780   }
1781 };
1782 
1783 /// Python wrapper for MlirOpResult.
1784 class PyOpResult : public PyConcreteValue<PyOpResult> {
1785 public:
1786   static constexpr IsAFunctionTy isaFunction = mlirValueIsAOpResult;
1787   static constexpr const char *pyClassName = "OpResult";
1788   using PyConcreteValue::PyConcreteValue;
1789 
1790   static void bindDerived(ClassTy &c) {
1791     c.def_property_readonly("owner", [](PyOpResult &self) {
1792       assert(
1793           mlirOperationEqual(self.getParentOperation()->get(),
1794                              mlirOpResultGetOwner(self.get())) &&
1795           "expected the owner of the value in Python to match that in the IR");
1796       return self.getParentOperation().getObject();
1797     });
1798     c.def_property_readonly("result_number", [](PyOpResult &self) {
1799       return mlirOpResultGetResultNumber(self.get());
1800     });
1801   }
1802 };
1803 
1804 /// Returns the list of types of the values held by container.
1805 template <typename Container>
1806 static std::vector<PyType> getValueTypes(Container &container,
1807                                          PyMlirContextRef &context) {
1808   std::vector<PyType> result;
1809   result.reserve(container.getNumElements());
1810   for (int i = 0, e = container.getNumElements(); i < e; ++i) {
1811     result.push_back(
1812         PyType(context, mlirValueGetType(container.getElement(i).get())));
1813   }
1814   return result;
1815 }
1816 
1817 /// A list of block arguments. Internally, these are stored as consecutive
1818 /// elements, random access is cheap. The argument list is associated with the
1819 /// operation that contains the block (detached blocks are not allowed in
1820 /// Python bindings) and extends its lifetime.
1821 class PyBlockArgumentList
1822     : public Sliceable<PyBlockArgumentList, PyBlockArgument> {
1823 public:
1824   static constexpr const char *pyClassName = "BlockArgumentList";
1825 
1826   PyBlockArgumentList(PyOperationRef operation, MlirBlock block,
1827                       intptr_t startIndex = 0, intptr_t length = -1,
1828                       intptr_t step = 1)
1829       : Sliceable(startIndex,
1830                   length == -1 ? mlirBlockGetNumArguments(block) : length,
1831                   step),
1832         operation(std::move(operation)), block(block) {}
1833 
1834   /// Returns the number of arguments in the list.
1835   intptr_t getNumElements() {
1836     operation->checkValid();
1837     return mlirBlockGetNumArguments(block);
1838   }
1839 
1840   /// Returns `pos`-the element in the list. Asserts on out-of-bounds.
1841   PyBlockArgument getElement(intptr_t pos) {
1842     MlirValue argument = mlirBlockGetArgument(block, pos);
1843     return PyBlockArgument(operation, argument);
1844   }
1845 
1846   /// Returns a sublist of this list.
1847   PyBlockArgumentList slice(intptr_t startIndex, intptr_t length,
1848                             intptr_t step) {
1849     return PyBlockArgumentList(operation, block, startIndex, length, step);
1850   }
1851 
1852   static void bindDerived(ClassTy &c) {
1853     c.def_property_readonly("types", [](PyBlockArgumentList &self) {
1854       return getValueTypes(self, self.operation->getContext());
1855     });
1856   }
1857 
1858 private:
1859   PyOperationRef operation;
1860   MlirBlock block;
1861 };
1862 
1863 /// A list of operation operands. Internally, these are stored as consecutive
1864 /// elements, random access is cheap. The result list is associated with the
1865 /// operation whose results these are, and extends the lifetime of this
1866 /// operation.
1867 class PyOpOperandList : public Sliceable<PyOpOperandList, PyValue> {
1868 public:
1869   static constexpr const char *pyClassName = "OpOperandList";
1870 
1871   PyOpOperandList(PyOperationRef operation, intptr_t startIndex = 0,
1872                   intptr_t length = -1, intptr_t step = 1)
1873       : Sliceable(startIndex,
1874                   length == -1 ? mlirOperationGetNumOperands(operation->get())
1875                                : length,
1876                   step),
1877         operation(operation) {}
1878 
1879   intptr_t getNumElements() {
1880     operation->checkValid();
1881     return mlirOperationGetNumOperands(operation->get());
1882   }
1883 
1884   PyValue getElement(intptr_t pos) {
1885     MlirValue operand = mlirOperationGetOperand(operation->get(), pos);
1886     MlirOperation owner;
1887     if (mlirValueIsAOpResult(operand))
1888       owner = mlirOpResultGetOwner(operand);
1889     else if (mlirValueIsABlockArgument(operand))
1890       owner = mlirBlockGetParentOperation(mlirBlockArgumentGetOwner(operand));
1891     else
1892       assert(false && "Value must be an block arg or op result.");
1893     PyOperationRef pyOwner =
1894         PyOperation::forOperation(operation->getContext(), owner);
1895     return PyValue(pyOwner, operand);
1896   }
1897 
1898   PyOpOperandList slice(intptr_t startIndex, intptr_t length, intptr_t step) {
1899     return PyOpOperandList(operation, startIndex, length, step);
1900   }
1901 
1902   void dunderSetItem(intptr_t index, PyValue value) {
1903     index = wrapIndex(index);
1904     mlirOperationSetOperand(operation->get(), index, value.get());
1905   }
1906 
1907   static void bindDerived(ClassTy &c) {
1908     c.def("__setitem__", &PyOpOperandList::dunderSetItem);
1909   }
1910 
1911 private:
1912   PyOperationRef operation;
1913 };
1914 
1915 /// A list of operation results. Internally, these are stored as consecutive
1916 /// elements, random access is cheap. The result list is associated with the
1917 /// operation whose results these are, and extends the lifetime of this
1918 /// operation.
1919 class PyOpResultList : public Sliceable<PyOpResultList, PyOpResult> {
1920 public:
1921   static constexpr const char *pyClassName = "OpResultList";
1922 
1923   PyOpResultList(PyOperationRef operation, intptr_t startIndex = 0,
1924                  intptr_t length = -1, intptr_t step = 1)
1925       : Sliceable(startIndex,
1926                   length == -1 ? mlirOperationGetNumResults(operation->get())
1927                                : length,
1928                   step),
1929         operation(operation) {}
1930 
1931   intptr_t getNumElements() {
1932     operation->checkValid();
1933     return mlirOperationGetNumResults(operation->get());
1934   }
1935 
1936   PyOpResult getElement(intptr_t index) {
1937     PyValue value(operation, mlirOperationGetResult(operation->get(), index));
1938     return PyOpResult(value);
1939   }
1940 
1941   PyOpResultList slice(intptr_t startIndex, intptr_t length, intptr_t step) {
1942     return PyOpResultList(operation, startIndex, length, step);
1943   }
1944 
1945   static void bindDerived(ClassTy &c) {
1946     c.def_property_readonly("types", [](PyOpResultList &self) {
1947       return getValueTypes(self, self.operation->getContext());
1948     });
1949   }
1950 
1951 private:
1952   PyOperationRef operation;
1953 };
1954 
1955 /// A list of operation attributes. Can be indexed by name, producing
1956 /// attributes, or by index, producing named attributes.
1957 class PyOpAttributeMap {
1958 public:
1959   PyOpAttributeMap(PyOperationRef operation)
1960       : operation(std::move(operation)) {}
1961 
1962   PyAttribute dunderGetItemNamed(const std::string &name) {
1963     MlirAttribute attr = mlirOperationGetAttributeByName(operation->get(),
1964                                                          toMlirStringRef(name));
1965     if (mlirAttributeIsNull(attr)) {
1966       throw SetPyError(PyExc_KeyError,
1967                        "attempt to access a non-existent attribute");
1968     }
1969     return PyAttribute(operation->getContext(), attr);
1970   }
1971 
1972   PyNamedAttribute dunderGetItemIndexed(intptr_t index) {
1973     if (index < 0 || index >= dunderLen()) {
1974       throw SetPyError(PyExc_IndexError,
1975                        "attempt to access out of bounds attribute");
1976     }
1977     MlirNamedAttribute namedAttr =
1978         mlirOperationGetAttribute(operation->get(), index);
1979     return PyNamedAttribute(
1980         namedAttr.attribute,
1981         std::string(mlirIdentifierStr(namedAttr.name).data,
1982                     mlirIdentifierStr(namedAttr.name).length));
1983   }
1984 
1985   void dunderSetItem(const std::string &name, const PyAttribute &attr) {
1986     mlirOperationSetAttributeByName(operation->get(), toMlirStringRef(name),
1987                                     attr);
1988   }
1989 
1990   void dunderDelItem(const std::string &name) {
1991     int removed = mlirOperationRemoveAttributeByName(operation->get(),
1992                                                      toMlirStringRef(name));
1993     if (!removed)
1994       throw SetPyError(PyExc_KeyError,
1995                        "attempt to delete a non-existent attribute");
1996   }
1997 
1998   intptr_t dunderLen() {
1999     return mlirOperationGetNumAttributes(operation->get());
2000   }
2001 
2002   bool dunderContains(const std::string &name) {
2003     return !mlirAttributeIsNull(mlirOperationGetAttributeByName(
2004         operation->get(), toMlirStringRef(name)));
2005   }
2006 
2007   static void bind(py::module &m) {
2008     py::class_<PyOpAttributeMap>(m, "OpAttributeMap", py::module_local())
2009         .def("__contains__", &PyOpAttributeMap::dunderContains)
2010         .def("__len__", &PyOpAttributeMap::dunderLen)
2011         .def("__getitem__", &PyOpAttributeMap::dunderGetItemNamed)
2012         .def("__getitem__", &PyOpAttributeMap::dunderGetItemIndexed)
2013         .def("__setitem__", &PyOpAttributeMap::dunderSetItem)
2014         .def("__delitem__", &PyOpAttributeMap::dunderDelItem);
2015   }
2016 
2017 private:
2018   PyOperationRef operation;
2019 };
2020 
2021 } // namespace
2022 
2023 //------------------------------------------------------------------------------
2024 // Populates the core exports of the 'ir' submodule.
2025 //------------------------------------------------------------------------------
2026 
2027 void mlir::python::populateIRCore(py::module &m) {
2028   //----------------------------------------------------------------------------
2029   // Mapping of MlirContext.
2030   //----------------------------------------------------------------------------
2031   py::class_<PyMlirContext>(m, "Context", py::module_local())
2032       .def(py::init<>(&PyMlirContext::createNewContextForInit))
2033       .def_static("_get_live_count", &PyMlirContext::getLiveCount)
2034       .def("_get_context_again",
2035            [](PyMlirContext &self) {
2036              PyMlirContextRef ref = PyMlirContext::forContext(self.get());
2037              return ref.releaseObject();
2038            })
2039       .def("_get_live_operation_count", &PyMlirContext::getLiveOperationCount)
2040       .def("_get_live_module_count", &PyMlirContext::getLiveModuleCount)
2041       .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR,
2042                              &PyMlirContext::getCapsule)
2043       .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyMlirContext::createFromCapsule)
2044       .def("__enter__", &PyMlirContext::contextEnter)
2045       .def("__exit__", &PyMlirContext::contextExit)
2046       .def_property_readonly_static(
2047           "current",
2048           [](py::object & /*class*/) {
2049             auto *context = PyThreadContextEntry::getDefaultContext();
2050             if (!context)
2051               throw SetPyError(PyExc_ValueError, "No current Context");
2052             return context;
2053           },
2054           "Gets the Context bound to the current thread or raises ValueError")
2055       .def_property_readonly(
2056           "dialects",
2057           [](PyMlirContext &self) { return PyDialects(self.getRef()); },
2058           "Gets a container for accessing dialects by name")
2059       .def_property_readonly(
2060           "d", [](PyMlirContext &self) { return PyDialects(self.getRef()); },
2061           "Alias for 'dialect'")
2062       .def(
2063           "get_dialect_descriptor",
2064           [=](PyMlirContext &self, std::string &name) {
2065             MlirDialect dialect = mlirContextGetOrLoadDialect(
2066                 self.get(), {name.data(), name.size()});
2067             if (mlirDialectIsNull(dialect)) {
2068               throw SetPyError(PyExc_ValueError,
2069                                Twine("Dialect '") + name + "' not found");
2070             }
2071             return PyDialectDescriptor(self.getRef(), dialect);
2072           },
2073           py::arg("dialect_name"),
2074           "Gets or loads a dialect by name, returning its descriptor object")
2075       .def_property(
2076           "allow_unregistered_dialects",
2077           [](PyMlirContext &self) -> bool {
2078             return mlirContextGetAllowUnregisteredDialects(self.get());
2079           },
2080           [](PyMlirContext &self, bool value) {
2081             mlirContextSetAllowUnregisteredDialects(self.get(), value);
2082           })
2083       .def(
2084           "enable_multithreading",
2085           [](PyMlirContext &self, bool enable) {
2086             mlirContextEnableMultithreading(self.get(), enable);
2087           },
2088           py::arg("enable"))
2089       .def(
2090           "is_registered_operation",
2091           [](PyMlirContext &self, std::string &name) {
2092             return mlirContextIsRegisteredOperation(
2093                 self.get(), MlirStringRef{name.data(), name.size()});
2094           },
2095           py::arg("operation_name"));
2096 
2097   //----------------------------------------------------------------------------
2098   // Mapping of PyDialectDescriptor
2099   //----------------------------------------------------------------------------
2100   py::class_<PyDialectDescriptor>(m, "DialectDescriptor", py::module_local())
2101       .def_property_readonly("namespace",
2102                              [](PyDialectDescriptor &self) {
2103                                MlirStringRef ns =
2104                                    mlirDialectGetNamespace(self.get());
2105                                return py::str(ns.data, ns.length);
2106                              })
2107       .def("__repr__", [](PyDialectDescriptor &self) {
2108         MlirStringRef ns = mlirDialectGetNamespace(self.get());
2109         std::string repr("<DialectDescriptor ");
2110         repr.append(ns.data, ns.length);
2111         repr.append(">");
2112         return repr;
2113       });
2114 
2115   //----------------------------------------------------------------------------
2116   // Mapping of PyDialects
2117   //----------------------------------------------------------------------------
2118   py::class_<PyDialects>(m, "Dialects", py::module_local())
2119       .def("__getitem__",
2120            [=](PyDialects &self, std::string keyName) {
2121              MlirDialect dialect =
2122                  self.getDialectForKey(keyName, /*attrError=*/false);
2123              py::object descriptor =
2124                  py::cast(PyDialectDescriptor{self.getContext(), dialect});
2125              return createCustomDialectWrapper(keyName, std::move(descriptor));
2126            })
2127       .def("__getattr__", [=](PyDialects &self, std::string attrName) {
2128         MlirDialect dialect =
2129             self.getDialectForKey(attrName, /*attrError=*/true);
2130         py::object descriptor =
2131             py::cast(PyDialectDescriptor{self.getContext(), dialect});
2132         return createCustomDialectWrapper(attrName, std::move(descriptor));
2133       });
2134 
2135   //----------------------------------------------------------------------------
2136   // Mapping of PyDialect
2137   //----------------------------------------------------------------------------
2138   py::class_<PyDialect>(m, "Dialect", py::module_local())
2139       .def(py::init<py::object>(), py::arg("descriptor"))
2140       .def_property_readonly(
2141           "descriptor", [](PyDialect &self) { return self.getDescriptor(); })
2142       .def("__repr__", [](py::object self) {
2143         auto clazz = self.attr("__class__");
2144         return py::str("<Dialect ") +
2145                self.attr("descriptor").attr("namespace") + py::str(" (class ") +
2146                clazz.attr("__module__") + py::str(".") +
2147                clazz.attr("__name__") + py::str(")>");
2148       });
2149 
2150   //----------------------------------------------------------------------------
2151   // Mapping of Location
2152   //----------------------------------------------------------------------------
2153   py::class_<PyLocation>(m, "Location", py::module_local())
2154       .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR, &PyLocation::getCapsule)
2155       .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyLocation::createFromCapsule)
2156       .def("__enter__", &PyLocation::contextEnter)
2157       .def("__exit__", &PyLocation::contextExit)
2158       .def("__eq__",
2159            [](PyLocation &self, PyLocation &other) -> bool {
2160              return mlirLocationEqual(self, other);
2161            })
2162       .def("__eq__", [](PyLocation &self, py::object other) { return false; })
2163       .def_property_readonly_static(
2164           "current",
2165           [](py::object & /*class*/) {
2166             auto *loc = PyThreadContextEntry::getDefaultLocation();
2167             if (!loc)
2168               throw SetPyError(PyExc_ValueError, "No current Location");
2169             return loc;
2170           },
2171           "Gets the Location bound to the current thread or raises ValueError")
2172       .def_static(
2173           "unknown",
2174           [](DefaultingPyMlirContext context) {
2175             return PyLocation(context->getRef(),
2176                               mlirLocationUnknownGet(context->get()));
2177           },
2178           py::arg("context") = py::none(),
2179           "Gets a Location representing an unknown location")
2180       .def_static(
2181           "callsite",
2182           [](PyLocation callee, const std::vector<PyLocation> &frames,
2183              DefaultingPyMlirContext context) {
2184             if (frames.empty())
2185               throw py::value_error("No caller frames provided");
2186             MlirLocation caller = frames.back().get();
2187             for (const PyLocation &frame :
2188                  llvm::reverse(llvm::makeArrayRef(frames).drop_back()))
2189               caller = mlirLocationCallSiteGet(frame.get(), caller);
2190             return PyLocation(context->getRef(),
2191                               mlirLocationCallSiteGet(callee.get(), caller));
2192           },
2193           py::arg("callee"), py::arg("frames"), py::arg("context") = py::none(),
2194           kContextGetCallSiteLocationDocstring)
2195       .def_static(
2196           "file",
2197           [](std::string filename, int line, int col,
2198              DefaultingPyMlirContext context) {
2199             return PyLocation(
2200                 context->getRef(),
2201                 mlirLocationFileLineColGet(
2202                     context->get(), toMlirStringRef(filename), line, col));
2203           },
2204           py::arg("filename"), py::arg("line"), py::arg("col"),
2205           py::arg("context") = py::none(), kContextGetFileLocationDocstring)
2206       .def_static(
2207           "fused",
2208           [](const std::vector<PyLocation> &pyLocations, llvm::Optional<PyAttribute> metadata,
2209              DefaultingPyMlirContext context) {
2210             if (pyLocations.empty())
2211               throw py::value_error("No locations provided");
2212             llvm::SmallVector<MlirLocation, 4> locations;
2213             locations.reserve(pyLocations.size());
2214             for (auto &pyLocation : pyLocations)
2215               locations.push_back(pyLocation.get());
2216             MlirLocation location = mlirLocationFusedGet(
2217                 context->get(), locations.size(), locations.data(),
2218                 metadata ? metadata->get() : MlirAttribute{0});
2219             return PyLocation(context->getRef(), location);
2220           },
2221           py::arg("locations"), py::arg("metadata") = py::none(),
2222           py::arg("context") = py::none(), kContextGetFusedLocationDocstring)
2223       .def_static(
2224           "name",
2225           [](std::string name, llvm::Optional<PyLocation> childLoc,
2226              DefaultingPyMlirContext context) {
2227             return PyLocation(
2228                 context->getRef(),
2229                 mlirLocationNameGet(
2230                     context->get(), toMlirStringRef(name),
2231                     childLoc ? childLoc->get()
2232                              : mlirLocationUnknownGet(context->get())));
2233           },
2234           py::arg("name"), py::arg("childLoc") = py::none(),
2235           py::arg("context") = py::none(), kContextGetNameLocationDocString)
2236       .def_property_readonly(
2237           "context",
2238           [](PyLocation &self) { return self.getContext().getObject(); },
2239           "Context that owns the Location")
2240       .def("__repr__", [](PyLocation &self) {
2241         PyPrintAccumulator printAccum;
2242         mlirLocationPrint(self, printAccum.getCallback(),
2243                           printAccum.getUserData());
2244         return printAccum.join();
2245       });
2246 
2247   //----------------------------------------------------------------------------
2248   // Mapping of Module
2249   //----------------------------------------------------------------------------
2250   py::class_<PyModule>(m, "Module", py::module_local())
2251       .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR, &PyModule::getCapsule)
2252       .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyModule::createFromCapsule)
2253       .def_static(
2254           "parse",
2255           [](const std::string moduleAsm, DefaultingPyMlirContext context) {
2256             MlirModule module = mlirModuleCreateParse(
2257                 context->get(), toMlirStringRef(moduleAsm));
2258             // TODO: Rework error reporting once diagnostic engine is exposed
2259             // in C API.
2260             if (mlirModuleIsNull(module)) {
2261               throw SetPyError(
2262                   PyExc_ValueError,
2263                   "Unable to parse module assembly (see diagnostics)");
2264             }
2265             return PyModule::forModule(module).releaseObject();
2266           },
2267           py::arg("asm"), py::arg("context") = py::none(),
2268           kModuleParseDocstring)
2269       .def_static(
2270           "create",
2271           [](DefaultingPyLocation loc) {
2272             MlirModule module = mlirModuleCreateEmpty(loc);
2273             return PyModule::forModule(module).releaseObject();
2274           },
2275           py::arg("loc") = py::none(), "Creates an empty module")
2276       .def_property_readonly(
2277           "context",
2278           [](PyModule &self) { return self.getContext().getObject(); },
2279           "Context that created the Module")
2280       .def_property_readonly(
2281           "operation",
2282           [](PyModule &self) {
2283             return PyOperation::forOperation(self.getContext(),
2284                                              mlirModuleGetOperation(self.get()),
2285                                              self.getRef().releaseObject())
2286                 .releaseObject();
2287           },
2288           "Accesses the module as an operation")
2289       .def_property_readonly(
2290           "body",
2291           [](PyModule &self) {
2292             PyOperationRef moduleOp = PyOperation::forOperation(
2293                 self.getContext(), mlirModuleGetOperation(self.get()),
2294                 self.getRef().releaseObject());
2295             PyBlock returnBlock(moduleOp, mlirModuleGetBody(self.get()));
2296             return returnBlock;
2297           },
2298           "Return the block for this module")
2299       .def(
2300           "dump",
2301           [](PyModule &self) {
2302             mlirOperationDump(mlirModuleGetOperation(self.get()));
2303           },
2304           kDumpDocstring)
2305       .def(
2306           "__str__",
2307           [](py::object self) {
2308             // Defer to the operation's __str__.
2309             return self.attr("operation").attr("__str__")();
2310           },
2311           kOperationStrDunderDocstring);
2312 
2313   //----------------------------------------------------------------------------
2314   // Mapping of Operation.
2315   //----------------------------------------------------------------------------
2316   py::class_<PyOperationBase>(m, "_OperationBase", py::module_local())
2317       .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR,
2318                              [](PyOperationBase &self) {
2319                                return self.getOperation().getCapsule();
2320                              })
2321       .def("__eq__",
2322            [](PyOperationBase &self, PyOperationBase &other) {
2323              return &self.getOperation() == &other.getOperation();
2324            })
2325       .def("__eq__",
2326            [](PyOperationBase &self, py::object other) { return false; })
2327       .def("__hash__",
2328            [](PyOperationBase &self) {
2329              return static_cast<size_t>(llvm::hash_value(&self.getOperation()));
2330            })
2331       .def_property_readonly("attributes",
2332                              [](PyOperationBase &self) {
2333                                return PyOpAttributeMap(
2334                                    self.getOperation().getRef());
2335                              })
2336       .def_property_readonly("operands",
2337                              [](PyOperationBase &self) {
2338                                return PyOpOperandList(
2339                                    self.getOperation().getRef());
2340                              })
2341       .def_property_readonly("regions",
2342                              [](PyOperationBase &self) {
2343                                return PyRegionList(
2344                                    self.getOperation().getRef());
2345                              })
2346       .def_property_readonly(
2347           "results",
2348           [](PyOperationBase &self) {
2349             return PyOpResultList(self.getOperation().getRef());
2350           },
2351           "Returns the list of Operation results.")
2352       .def_property_readonly(
2353           "result",
2354           [](PyOperationBase &self) {
2355             auto &operation = self.getOperation();
2356             auto numResults = mlirOperationGetNumResults(operation);
2357             if (numResults != 1) {
2358               auto name = mlirIdentifierStr(mlirOperationGetName(operation));
2359               throw SetPyError(
2360                   PyExc_ValueError,
2361                   Twine("Cannot call .result on operation ") +
2362                       StringRef(name.data, name.length) + " which has " +
2363                       Twine(numResults) +
2364                       " results (it is only valid for operations with a "
2365                       "single result)");
2366             }
2367             return PyOpResult(operation.getRef(),
2368                               mlirOperationGetResult(operation, 0));
2369           },
2370           "Shortcut to get an op result if it has only one (throws an error "
2371           "otherwise).")
2372       .def_property_readonly(
2373           "location",
2374           [](PyOperationBase &self) {
2375             PyOperation &operation = self.getOperation();
2376             return PyLocation(operation.getContext(),
2377                               mlirOperationGetLocation(operation.get()));
2378           },
2379           "Returns the source location the operation was defined or derived "
2380           "from.")
2381       .def(
2382           "__str__",
2383           [](PyOperationBase &self) {
2384             return self.getAsm(/*binary=*/false,
2385                                /*largeElementsLimit=*/llvm::None,
2386                                /*enableDebugInfo=*/false,
2387                                /*prettyDebugInfo=*/false,
2388                                /*printGenericOpForm=*/false,
2389                                /*useLocalScope=*/false,
2390                                /*assumeVerified=*/false);
2391           },
2392           "Returns the assembly form of the operation.")
2393       .def("print", &PyOperationBase::print,
2394            // Careful: Lots of arguments must match up with print method.
2395            py::arg("file") = py::none(), py::arg("binary") = false,
2396            py::arg("large_elements_limit") = py::none(),
2397            py::arg("enable_debug_info") = false,
2398            py::arg("pretty_debug_info") = false,
2399            py::arg("print_generic_op_form") = false,
2400            py::arg("use_local_scope") = false,
2401            py::arg("assume_verified") = false, kOperationPrintDocstring)
2402       .def("get_asm", &PyOperationBase::getAsm,
2403            // Careful: Lots of arguments must match up with get_asm method.
2404            py::arg("binary") = false,
2405            py::arg("large_elements_limit") = py::none(),
2406            py::arg("enable_debug_info") = false,
2407            py::arg("pretty_debug_info") = false,
2408            py::arg("print_generic_op_form") = false,
2409            py::arg("use_local_scope") = false,
2410            py::arg("assume_verified") = false, kOperationGetAsmDocstring)
2411       .def(
2412           "verify",
2413           [](PyOperationBase &self) {
2414             return mlirOperationVerify(self.getOperation());
2415           },
2416           "Verify the operation and return true if it passes, false if it "
2417           "fails.")
2418       .def("move_after", &PyOperationBase::moveAfter, py::arg("other"),
2419            "Puts self immediately after the other operation in its parent "
2420            "block.")
2421       .def("move_before", &PyOperationBase::moveBefore, py::arg("other"),
2422            "Puts self immediately before the other operation in its parent "
2423            "block.")
2424       .def(
2425           "detach_from_parent",
2426           [](PyOperationBase &self) {
2427             PyOperation &operation = self.getOperation();
2428             operation.checkValid();
2429             if (!operation.isAttached())
2430               throw py::value_error("Detached operation has no parent.");
2431 
2432             operation.detachFromParent();
2433             return operation.createOpView();
2434           },
2435           "Detaches the operation from its parent block.");
2436 
2437   py::class_<PyOperation, PyOperationBase>(m, "Operation", py::module_local())
2438       .def_static("create", &PyOperation::create, py::arg("name"),
2439                   py::arg("results") = py::none(),
2440                   py::arg("operands") = py::none(),
2441                   py::arg("attributes") = py::none(),
2442                   py::arg("successors") = py::none(), py::arg("regions") = 0,
2443                   py::arg("loc") = py::none(), py::arg("ip") = py::none(),
2444                   kOperationCreateDocstring)
2445       .def_property_readonly("parent",
2446                              [](PyOperation &self) -> py::object {
2447                                auto parent = self.getParentOperation();
2448                                if (parent)
2449                                  return parent->getObject();
2450                                return py::none();
2451                              })
2452       .def("erase", &PyOperation::erase)
2453       .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR,
2454                              &PyOperation::getCapsule)
2455       .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyOperation::createFromCapsule)
2456       .def_property_readonly("name",
2457                              [](PyOperation &self) {
2458                                self.checkValid();
2459                                MlirOperation operation = self.get();
2460                                MlirStringRef name = mlirIdentifierStr(
2461                                    mlirOperationGetName(operation));
2462                                return py::str(name.data, name.length);
2463                              })
2464       .def_property_readonly(
2465           "context",
2466           [](PyOperation &self) {
2467             self.checkValid();
2468             return self.getContext().getObject();
2469           },
2470           "Context that owns the Operation")
2471       .def_property_readonly("opview", &PyOperation::createOpView);
2472 
2473   auto opViewClass =
2474       py::class_<PyOpView, PyOperationBase>(m, "OpView", py::module_local())
2475           .def(py::init<py::object>(), py::arg("operation"))
2476           .def_property_readonly("operation", &PyOpView::getOperationObject)
2477           .def_property_readonly(
2478               "context",
2479               [](PyOpView &self) {
2480                 return self.getOperation().getContext().getObject();
2481               },
2482               "Context that owns the Operation")
2483           .def("__str__", [](PyOpView &self) {
2484             return py::str(self.getOperationObject());
2485           });
2486   opViewClass.attr("_ODS_REGIONS") = py::make_tuple(0, true);
2487   opViewClass.attr("_ODS_OPERAND_SEGMENTS") = py::none();
2488   opViewClass.attr("_ODS_RESULT_SEGMENTS") = py::none();
2489   opViewClass.attr("build_generic") = classmethod(
2490       &PyOpView::buildGeneric, py::arg("cls"), py::arg("results") = py::none(),
2491       py::arg("operands") = py::none(), py::arg("attributes") = py::none(),
2492       py::arg("successors") = py::none(), py::arg("regions") = py::none(),
2493       py::arg("loc") = py::none(), py::arg("ip") = py::none(),
2494       "Builds a specific, generated OpView based on class level attributes.");
2495 
2496   //----------------------------------------------------------------------------
2497   // Mapping of PyRegion.
2498   //----------------------------------------------------------------------------
2499   py::class_<PyRegion>(m, "Region", py::module_local())
2500       .def_property_readonly(
2501           "blocks",
2502           [](PyRegion &self) {
2503             return PyBlockList(self.getParentOperation(), self.get());
2504           },
2505           "Returns a forward-optimized sequence of blocks.")
2506       .def_property_readonly(
2507           "owner",
2508           [](PyRegion &self) {
2509             return self.getParentOperation()->createOpView();
2510           },
2511           "Returns the operation owning this region.")
2512       .def(
2513           "__iter__",
2514           [](PyRegion &self) {
2515             self.checkValid();
2516             MlirBlock firstBlock = mlirRegionGetFirstBlock(self.get());
2517             return PyBlockIterator(self.getParentOperation(), firstBlock);
2518           },
2519           "Iterates over blocks in the region.")
2520       .def("__eq__",
2521            [](PyRegion &self, PyRegion &other) {
2522              return self.get().ptr == other.get().ptr;
2523            })
2524       .def("__eq__", [](PyRegion &self, py::object &other) { return false; });
2525 
2526   //----------------------------------------------------------------------------
2527   // Mapping of PyBlock.
2528   //----------------------------------------------------------------------------
2529   py::class_<PyBlock>(m, "Block", py::module_local())
2530       .def_property_readonly(
2531           "owner",
2532           [](PyBlock &self) {
2533             return self.getParentOperation()->createOpView();
2534           },
2535           "Returns the owning operation of this block.")
2536       .def_property_readonly(
2537           "region",
2538           [](PyBlock &self) {
2539             MlirRegion region = mlirBlockGetParentRegion(self.get());
2540             return PyRegion(self.getParentOperation(), region);
2541           },
2542           "Returns the owning region of this block.")
2543       .def_property_readonly(
2544           "arguments",
2545           [](PyBlock &self) {
2546             return PyBlockArgumentList(self.getParentOperation(), self.get());
2547           },
2548           "Returns a list of block arguments.")
2549       .def_property_readonly(
2550           "operations",
2551           [](PyBlock &self) {
2552             return PyOperationList(self.getParentOperation(), self.get());
2553           },
2554           "Returns a forward-optimized sequence of operations.")
2555       .def_static(
2556           "create_at_start",
2557           [](PyRegion &parent, py::list pyArgTypes) {
2558             parent.checkValid();
2559             llvm::SmallVector<MlirType, 4> argTypes;
2560             argTypes.reserve(pyArgTypes.size());
2561             for (auto &pyArg : pyArgTypes) {
2562               argTypes.push_back(pyArg.cast<PyType &>());
2563             }
2564 
2565             MlirBlock block = mlirBlockCreate(argTypes.size(), argTypes.data());
2566             mlirRegionInsertOwnedBlock(parent, 0, block);
2567             return PyBlock(parent.getParentOperation(), block);
2568           },
2569           py::arg("parent"), py::arg("arg_types") = py::list(),
2570           "Creates and returns a new Block at the beginning of the given "
2571           "region (with given argument types).")
2572       .def(
2573           "create_before",
2574           [](PyBlock &self, py::args pyArgTypes) {
2575             self.checkValid();
2576             llvm::SmallVector<MlirType, 4> argTypes;
2577             argTypes.reserve(pyArgTypes.size());
2578             for (auto &pyArg : pyArgTypes) {
2579               argTypes.push_back(pyArg.cast<PyType &>());
2580             }
2581 
2582             MlirBlock block = mlirBlockCreate(argTypes.size(), argTypes.data());
2583             MlirRegion region = mlirBlockGetParentRegion(self.get());
2584             mlirRegionInsertOwnedBlockBefore(region, self.get(), block);
2585             return PyBlock(self.getParentOperation(), block);
2586           },
2587           "Creates and returns a new Block before this block "
2588           "(with given argument types).")
2589       .def(
2590           "create_after",
2591           [](PyBlock &self, py::args pyArgTypes) {
2592             self.checkValid();
2593             llvm::SmallVector<MlirType, 4> argTypes;
2594             argTypes.reserve(pyArgTypes.size());
2595             for (auto &pyArg : pyArgTypes) {
2596               argTypes.push_back(pyArg.cast<PyType &>());
2597             }
2598 
2599             MlirBlock block = mlirBlockCreate(argTypes.size(), argTypes.data());
2600             MlirRegion region = mlirBlockGetParentRegion(self.get());
2601             mlirRegionInsertOwnedBlockAfter(region, self.get(), block);
2602             return PyBlock(self.getParentOperation(), block);
2603           },
2604           "Creates and returns a new Block after this block "
2605           "(with given argument types).")
2606       .def(
2607           "__iter__",
2608           [](PyBlock &self) {
2609             self.checkValid();
2610             MlirOperation firstOperation =
2611                 mlirBlockGetFirstOperation(self.get());
2612             return PyOperationIterator(self.getParentOperation(),
2613                                        firstOperation);
2614           },
2615           "Iterates over operations in the block.")
2616       .def("__eq__",
2617            [](PyBlock &self, PyBlock &other) {
2618              return self.get().ptr == other.get().ptr;
2619            })
2620       .def("__eq__", [](PyBlock &self, py::object &other) { return false; })
2621       .def(
2622           "__str__",
2623           [](PyBlock &self) {
2624             self.checkValid();
2625             PyPrintAccumulator printAccum;
2626             mlirBlockPrint(self.get(), printAccum.getCallback(),
2627                            printAccum.getUserData());
2628             return printAccum.join();
2629           },
2630           "Returns the assembly form of the block.")
2631       .def(
2632           "append",
2633           [](PyBlock &self, PyOperationBase &operation) {
2634             if (operation.getOperation().isAttached())
2635               operation.getOperation().detachFromParent();
2636 
2637             MlirOperation mlirOperation = operation.getOperation().get();
2638             mlirBlockAppendOwnedOperation(self.get(), mlirOperation);
2639             operation.getOperation().setAttached(
2640                 self.getParentOperation().getObject());
2641           },
2642           py::arg("operation"),
2643           "Appends an operation to this block. If the operation is currently "
2644           "in another block, it will be moved.");
2645 
2646   //----------------------------------------------------------------------------
2647   // Mapping of PyInsertionPoint.
2648   //----------------------------------------------------------------------------
2649 
2650   py::class_<PyInsertionPoint>(m, "InsertionPoint", py::module_local())
2651       .def(py::init<PyBlock &>(), py::arg("block"),
2652            "Inserts after the last operation but still inside the block.")
2653       .def("__enter__", &PyInsertionPoint::contextEnter)
2654       .def("__exit__", &PyInsertionPoint::contextExit)
2655       .def_property_readonly_static(
2656           "current",
2657           [](py::object & /*class*/) {
2658             auto *ip = PyThreadContextEntry::getDefaultInsertionPoint();
2659             if (!ip)
2660               throw SetPyError(PyExc_ValueError, "No current InsertionPoint");
2661             return ip;
2662           },
2663           "Gets the InsertionPoint bound to the current thread or raises "
2664           "ValueError if none has been set")
2665       .def(py::init<PyOperationBase &>(), py::arg("beforeOperation"),
2666            "Inserts before a referenced operation.")
2667       .def_static("at_block_begin", &PyInsertionPoint::atBlockBegin,
2668                   py::arg("block"), "Inserts at the beginning of the block.")
2669       .def_static("at_block_terminator", &PyInsertionPoint::atBlockTerminator,
2670                   py::arg("block"), "Inserts before the block terminator.")
2671       .def("insert", &PyInsertionPoint::insert, py::arg("operation"),
2672            "Inserts an operation.")
2673       .def_property_readonly(
2674           "block", [](PyInsertionPoint &self) { return self.getBlock(); },
2675           "Returns the block that this InsertionPoint points to.");
2676 
2677   //----------------------------------------------------------------------------
2678   // Mapping of PyAttribute.
2679   //----------------------------------------------------------------------------
2680   py::class_<PyAttribute>(m, "Attribute", py::module_local())
2681       // Delegate to the PyAttribute copy constructor, which will also lifetime
2682       // extend the backing context which owns the MlirAttribute.
2683       .def(py::init<PyAttribute &>(), py::arg("cast_from_type"),
2684            "Casts the passed attribute to the generic Attribute")
2685       .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR,
2686                              &PyAttribute::getCapsule)
2687       .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyAttribute::createFromCapsule)
2688       .def_static(
2689           "parse",
2690           [](std::string attrSpec, DefaultingPyMlirContext context) {
2691             MlirAttribute type = mlirAttributeParseGet(
2692                 context->get(), toMlirStringRef(attrSpec));
2693             // TODO: Rework error reporting once diagnostic engine is exposed
2694             // in C API.
2695             if (mlirAttributeIsNull(type)) {
2696               throw SetPyError(PyExc_ValueError,
2697                                Twine("Unable to parse attribute: '") +
2698                                    attrSpec + "'");
2699             }
2700             return PyAttribute(context->getRef(), type);
2701           },
2702           py::arg("asm"), py::arg("context") = py::none(),
2703           "Parses an attribute from an assembly form")
2704       .def_property_readonly(
2705           "context",
2706           [](PyAttribute &self) { return self.getContext().getObject(); },
2707           "Context that owns the Attribute")
2708       .def_property_readonly("type",
2709                              [](PyAttribute &self) {
2710                                return PyType(self.getContext()->getRef(),
2711                                              mlirAttributeGetType(self));
2712                              })
2713       .def(
2714           "get_named",
2715           [](PyAttribute &self, std::string name) {
2716             return PyNamedAttribute(self, std::move(name));
2717           },
2718           py::keep_alive<0, 1>(), "Binds a name to the attribute")
2719       .def("__eq__",
2720            [](PyAttribute &self, PyAttribute &other) { return self == other; })
2721       .def("__eq__", [](PyAttribute &self, py::object &other) { return false; })
2722       .def("__hash__",
2723            [](PyAttribute &self) {
2724              return static_cast<size_t>(llvm::hash_value(self.get().ptr));
2725            })
2726       .def(
2727           "dump", [](PyAttribute &self) { mlirAttributeDump(self); },
2728           kDumpDocstring)
2729       .def(
2730           "__str__",
2731           [](PyAttribute &self) {
2732             PyPrintAccumulator printAccum;
2733             mlirAttributePrint(self, printAccum.getCallback(),
2734                                printAccum.getUserData());
2735             return printAccum.join();
2736           },
2737           "Returns the assembly form of the Attribute.")
2738       .def("__repr__", [](PyAttribute &self) {
2739         // Generally, assembly formats are not printed for __repr__ because
2740         // this can cause exceptionally long debug output and exceptions.
2741         // However, attribute values are generally considered useful and are
2742         // printed. This may need to be re-evaluated if debug dumps end up
2743         // being excessive.
2744         PyPrintAccumulator printAccum;
2745         printAccum.parts.append("Attribute(");
2746         mlirAttributePrint(self, printAccum.getCallback(),
2747                            printAccum.getUserData());
2748         printAccum.parts.append(")");
2749         return printAccum.join();
2750       });
2751 
2752   //----------------------------------------------------------------------------
2753   // Mapping of PyNamedAttribute
2754   //----------------------------------------------------------------------------
2755   py::class_<PyNamedAttribute>(m, "NamedAttribute", py::module_local())
2756       .def("__repr__",
2757            [](PyNamedAttribute &self) {
2758              PyPrintAccumulator printAccum;
2759              printAccum.parts.append("NamedAttribute(");
2760              printAccum.parts.append(
2761                  py::str(mlirIdentifierStr(self.namedAttr.name).data,
2762                          mlirIdentifierStr(self.namedAttr.name).length));
2763              printAccum.parts.append("=");
2764              mlirAttributePrint(self.namedAttr.attribute,
2765                                 printAccum.getCallback(),
2766                                 printAccum.getUserData());
2767              printAccum.parts.append(")");
2768              return printAccum.join();
2769            })
2770       .def_property_readonly(
2771           "name",
2772           [](PyNamedAttribute &self) {
2773             return py::str(mlirIdentifierStr(self.namedAttr.name).data,
2774                            mlirIdentifierStr(self.namedAttr.name).length);
2775           },
2776           "The name of the NamedAttribute binding")
2777       .def_property_readonly(
2778           "attr",
2779           [](PyNamedAttribute &self) {
2780             // TODO: When named attribute is removed/refactored, also remove
2781             // this constructor (it does an inefficient table lookup).
2782             auto contextRef = PyMlirContext::forContext(
2783                 mlirAttributeGetContext(self.namedAttr.attribute));
2784             return PyAttribute(std::move(contextRef), self.namedAttr.attribute);
2785           },
2786           py::keep_alive<0, 1>(),
2787           "The underlying generic attribute of the NamedAttribute binding");
2788 
2789   //----------------------------------------------------------------------------
2790   // Mapping of PyType.
2791   //----------------------------------------------------------------------------
2792   py::class_<PyType>(m, "Type", py::module_local())
2793       // Delegate to the PyType copy constructor, which will also lifetime
2794       // extend the backing context which owns the MlirType.
2795       .def(py::init<PyType &>(), py::arg("cast_from_type"),
2796            "Casts the passed type to the generic Type")
2797       .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR, &PyType::getCapsule)
2798       .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyType::createFromCapsule)
2799       .def_static(
2800           "parse",
2801           [](std::string typeSpec, DefaultingPyMlirContext context) {
2802             MlirType type =
2803                 mlirTypeParseGet(context->get(), toMlirStringRef(typeSpec));
2804             // TODO: Rework error reporting once diagnostic engine is exposed
2805             // in C API.
2806             if (mlirTypeIsNull(type)) {
2807               throw SetPyError(PyExc_ValueError,
2808                                Twine("Unable to parse type: '") + typeSpec +
2809                                    "'");
2810             }
2811             return PyType(context->getRef(), type);
2812           },
2813           py::arg("asm"), py::arg("context") = py::none(),
2814           kContextParseTypeDocstring)
2815       .def_property_readonly(
2816           "context", [](PyType &self) { return self.getContext().getObject(); },
2817           "Context that owns the Type")
2818       .def("__eq__", [](PyType &self, PyType &other) { return self == other; })
2819       .def("__eq__", [](PyType &self, py::object &other) { return false; })
2820       .def("__hash__",
2821            [](PyType &self) {
2822              return static_cast<size_t>(llvm::hash_value(self.get().ptr));
2823            })
2824       .def(
2825           "dump", [](PyType &self) { mlirTypeDump(self); }, kDumpDocstring)
2826       .def(
2827           "__str__",
2828           [](PyType &self) {
2829             PyPrintAccumulator printAccum;
2830             mlirTypePrint(self, printAccum.getCallback(),
2831                           printAccum.getUserData());
2832             return printAccum.join();
2833           },
2834           "Returns the assembly form of the type.")
2835       .def("__repr__", [](PyType &self) {
2836         // Generally, assembly formats are not printed for __repr__ because
2837         // this can cause exceptionally long debug output and exceptions.
2838         // However, types are an exception as they typically have compact
2839         // assembly forms and printing them is useful.
2840         PyPrintAccumulator printAccum;
2841         printAccum.parts.append("Type(");
2842         mlirTypePrint(self, printAccum.getCallback(), printAccum.getUserData());
2843         printAccum.parts.append(")");
2844         return printAccum.join();
2845       });
2846 
2847   //----------------------------------------------------------------------------
2848   // Mapping of Value.
2849   //----------------------------------------------------------------------------
2850   py::class_<PyValue>(m, "Value", py::module_local())
2851       .def_property_readonly(MLIR_PYTHON_CAPI_PTR_ATTR, &PyValue::getCapsule)
2852       .def(MLIR_PYTHON_CAPI_FACTORY_ATTR, &PyValue::createFromCapsule)
2853       .def_property_readonly(
2854           "context",
2855           [](PyValue &self) { return self.getParentOperation()->getContext(); },
2856           "Context in which the value lives.")
2857       .def(
2858           "dump", [](PyValue &self) { mlirValueDump(self.get()); },
2859           kDumpDocstring)
2860       .def_property_readonly(
2861           "owner",
2862           [](PyValue &self) {
2863             assert(mlirOperationEqual(self.getParentOperation()->get(),
2864                                       mlirOpResultGetOwner(self.get())) &&
2865                    "expected the owner of the value in Python to match that in "
2866                    "the IR");
2867             return self.getParentOperation().getObject();
2868           })
2869       .def("__eq__",
2870            [](PyValue &self, PyValue &other) {
2871              return self.get().ptr == other.get().ptr;
2872            })
2873       .def("__eq__", [](PyValue &self, py::object other) { return false; })
2874       .def("__hash__",
2875            [](PyValue &self) {
2876              return static_cast<size_t>(llvm::hash_value(self.get().ptr));
2877            })
2878       .def(
2879           "__str__",
2880           [](PyValue &self) {
2881             PyPrintAccumulator printAccum;
2882             printAccum.parts.append("Value(");
2883             mlirValuePrint(self.get(), printAccum.getCallback(),
2884                            printAccum.getUserData());
2885             printAccum.parts.append(")");
2886             return printAccum.join();
2887           },
2888           kValueDunderStrDocstring)
2889       .def_property_readonly("type", [](PyValue &self) {
2890         return PyType(self.getParentOperation()->getContext(),
2891                       mlirValueGetType(self.get()));
2892       });
2893   PyBlockArgument::bind(m);
2894   PyOpResult::bind(m);
2895 
2896   //----------------------------------------------------------------------------
2897   // Mapping of SymbolTable.
2898   //----------------------------------------------------------------------------
2899   py::class_<PySymbolTable>(m, "SymbolTable", py::module_local())
2900       .def(py::init<PyOperationBase &>())
2901       .def("__getitem__", &PySymbolTable::dunderGetItem)
2902       .def("insert", &PySymbolTable::insert, py::arg("operation"))
2903       .def("erase", &PySymbolTable::erase, py::arg("operation"))
2904       .def("__delitem__", &PySymbolTable::dunderDel)
2905       .def("__contains__",
2906            [](PySymbolTable &table, const std::string &name) {
2907              return !mlirOperationIsNull(mlirSymbolTableLookup(
2908                  table, mlirStringRefCreate(name.data(), name.length())));
2909            })
2910       // Static helpers.
2911       .def_static("set_symbol_name", &PySymbolTable::setSymbolName,
2912                   py::arg("symbol"), py::arg("name"))
2913       .def_static("get_symbol_name", &PySymbolTable::getSymbolName,
2914                   py::arg("symbol"))
2915       .def_static("get_visibility", &PySymbolTable::getVisibility,
2916                   py::arg("symbol"))
2917       .def_static("set_visibility", &PySymbolTable::setVisibility,
2918                   py::arg("symbol"), py::arg("visibility"))
2919       .def_static("replace_all_symbol_uses",
2920                   &PySymbolTable::replaceAllSymbolUses, py::arg("old_symbol"),
2921                   py::arg("new_symbol"), py::arg("from_op"))
2922       .def_static("walk_symbol_tables", &PySymbolTable::walkSymbolTables,
2923                   py::arg("from_op"), py::arg("all_sym_uses_visible"),
2924                   py::arg("callback"));
2925 
2926   // Container bindings.
2927   PyBlockArgumentList::bind(m);
2928   PyBlockIterator::bind(m);
2929   PyBlockList::bind(m);
2930   PyOperationIterator::bind(m);
2931   PyOperationList::bind(m);
2932   PyOpAttributeMap::bind(m);
2933   PyOpOperandList::bind(m);
2934   PyOpResultList::bind(m);
2935   PyRegionIterator::bind(m);
2936   PyRegionList::bind(m);
2937 
2938   // Debug bindings.
2939   PyGlobalDebugFlag::bind(m);
2940 }
2941