MLIR 24.0.0git
IRCore.cpp
Go to the documentation of this file.
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// clang-format off
13#include "mlir-c/Bindings/Python/Interop.h" // This is expected after nanobind.
14// clang-format on
16#include "mlir-c/Debug.h"
17#include "mlir-c/Diagnostics.h"
19#include "mlir-c/IR.h"
20#include "mlir-c/Support.h"
21
22#include <array>
23#include <cassert>
24#include <functional>
25#include <optional>
26#include <string>
27
28namespace nb = nanobind;
29using namespace nb::literals;
30using namespace mlir;
32
33static const char kModuleParseDocstring[] =
34 R"(Parses a module's assembly format from a string.
35
36Returns a new MlirModule or raises an MLIRError if the parsing fails.
37
38See also: https://mlir.llvm.org/docs/LangRef/
39)";
40
41static const char kDumpDocstring[] =
42 "Dumps a debug representation of the object to stderr.";
43
45 R"(Replace all uses of this value with the `with` value, except for those
46in `exceptions`. `exceptions` can be either a single operation or a list of
47operations.
48)";
49
50//------------------------------------------------------------------------------
51// Utilities.
52//------------------------------------------------------------------------------
53
54/// Local helper to compute std::hash for a value.
55template <typename T>
56static size_t hash(const T &value) {
57 return std::hash<T>{}(value);
58}
59
60static nb::object
61createCustomDialectWrapper(const std::string &dialectNamespace,
62 nb::object dialectDescriptor) {
63 auto dialectClass =
65 dialectNamespace);
66 if (!dialectClass) {
67 // Use the base class.
69 std::move(dialectDescriptor)));
70 }
71
72 // Create the custom implementation.
73 return (*dialectClass)(std::move(dialectDescriptor));
74}
75
76namespace mlir {
77namespace python {
79
80MlirBlock createBlock(
81 const nb::typed<nb::sequence, PyType> &pyArgTypes,
82 const std::optional<nb::typed<nb::sequence, PyLocation>> &pyArgLocs) {
83 std::vector<MlirType> argTypes;
84 argTypes.reserve(nb::len(pyArgTypes));
85 for (nb::handle pyType : pyArgTypes)
86 argTypes.push_back(
87 nb::cast<python::MLIR_BINDINGS_PYTHON_DOMAIN::PyType &>(pyType));
88
89 std::vector<MlirLocation> argLocs;
90 if (pyArgLocs) {
91 argLocs.reserve(nb::len(*pyArgLocs));
92 for (nb::handle pyLoc : *pyArgLocs)
93 argLocs.push_back(
94 nb::cast<python::MLIR_BINDINGS_PYTHON_DOMAIN::PyLocation &>(pyLoc));
95 } else if (!argTypes.empty()) {
96 argLocs.assign(
97 argTypes.size(),
99 }
100
101 if (argTypes.size() != argLocs.size())
102 throw nb::value_error(
103 join("Expected ", argTypes.size(), " locations, got: ", argLocs.size())
104 .c_str());
105 return mlirBlockCreate(argTypes.size(), argTypes.data(), argLocs.data());
106}
107
108void PyGlobalDebugFlag::set(nb::object &o, bool enable) {
109 nb::ft_lock_guard lock(mutex);
110 mlirEnableGlobalDebug(enable);
111}
112
113bool PyGlobalDebugFlag::get(const nb::object &) {
114 nb::ft_lock_guard lock(mutex);
116}
117
118void PyGlobalDebugFlag::bind(nb::module_ &m) {
119 // Debug flags.
120 nb::class_<PyGlobalDebugFlag>(m, "_GlobalDebug")
121 .def_prop_rw_static("flag", &PyGlobalDebugFlag::get,
122 &PyGlobalDebugFlag::set, "LLVM-wide debug flag.")
123 .def_static(
124 "set_types",
125 [](const std::string &type) {
126 nb::ft_lock_guard lock(mutex);
127 mlirSetGlobalDebugType(type.c_str());
128 },
129 "types"_a, "Sets specific debug types to be produced by LLVM.")
130 .def_static(
131 "set_types",
132 [](const std::vector<std::string> &types) {
133 std::vector<const char *> pointers;
134 pointers.reserve(types.size());
135 for (const std::string &str : types)
136 pointers.push_back(str.c_str());
137 nb::ft_lock_guard lock(mutex);
138 mlirSetGlobalDebugTypes(pointers.data(), pointers.size());
139 },
140 "types"_a,
141 "Sets multiple specific debug types to be produced by LLVM.");
142}
143
144nb::ft_mutex PyGlobalDebugFlag::mutex;
145
146bool PyAttrBuilderMap::dunderContains(const std::string &attributeKind) {
147 return PyGlobals::get().lookupAttributeBuilder(attributeKind).has_value();
148}
149
150nb::callable
151PyAttrBuilderMap::dunderGetItemNamed(const std::string &attributeKind) {
152 auto builder = PyGlobals::get().lookupAttributeBuilder(attributeKind);
153 if (!builder)
154 throw nb::key_error(attributeKind.c_str());
155 return *builder;
156}
157
158void PyAttrBuilderMap::dunderSetItemNamed(const std::string &attributeKind,
159 nb::callable func, bool replace,
160 bool allow_existing) {
161 PyGlobals::get().registerAttributeBuilder(attributeKind, std::move(func),
162 replace, allow_existing);
163}
164
165void PyAttrBuilderMap::bind(nb::module_ &m) {
166 nb::class_<PyAttrBuilderMap>(m, "AttrBuilder")
167 .def_static("contains", &PyAttrBuilderMap::dunderContains,
168 "attribute_kind"_a,
169 "Checks whether an attribute builder is registered for the "
170 "given attribute kind.")
171 .def_static("get", &PyAttrBuilderMap::dunderGetItemNamed,
172 "attribute_kind"_a,
173 "Gets the registered attribute builder for the given "
174 "attribute kind.")
175 .def_static("insert", &PyAttrBuilderMap::dunderSetItemNamed,
176 "attribute_kind"_a, "attr_builder"_a, "replace"_a = false,
177 "allow_existing"_a = false,
178 "Register an attribute builder for building MLIR "
179 "attributes from Python values.");
180}
181
182//------------------------------------------------------------------------------
183// PyBlock
184//------------------------------------------------------------------------------
185
187 return nb::steal<nb::object>(mlirPythonBlockToCapsule(get()));
188}
189
190//------------------------------------------------------------------------------
191// Collections.
192//------------------------------------------------------------------------------
193
197 length == -1 ? mlirOperationGetNumRegions(operation->get())
198 : length,
199 step),
200 operation(std::move(operation)) {}
201
202intptr_t PyRegionList::getRawNumElements() {
203 operation->checkValid();
204 return mlirOperationGetNumRegions(operation->get());
205}
206
207PyRegion PyRegionList::getRawElement(intptr_t pos) {
208 operation->checkValid();
209 return PyRegion(operation, mlirOperationGetRegion(operation->get(), pos));
210}
211
212PyRegionList PyRegionList::slice(intptr_t startIndex, intptr_t length,
213 intptr_t step) const {
214 return PyRegionList(operation, startIndex, length, step);
215}
216
217nb::typed<nb::object, PyBlock> PyBlockIterator::dunderNext() {
218 operation->checkValid();
219 if (mlirBlockIsNull(next)) {
220 PyErr_SetNone(PyExc_StopIteration);
221 // python functions should return NULL after setting any exception
222 return nb::object();
223 }
224
225 PyBlock returnBlock(operation, next);
226 next = mlirBlockGetNextInRegion(next);
227 return nb::cast(returnBlock);
228}
229
230void PyBlockIterator::bind(nb::module_ &m) {
231 nb::class_<PyBlockIterator>(m, "BlockIterator")
232 .def("__iter__", &PyBlockIterator::dunderIter,
233 "Returns an iterator over the blocks in the operation's region.")
234 .def("__next__", &PyBlockIterator::dunderNext,
235 "Returns the next block in the iteration.");
236}
237
239 operation->checkValid();
240 return PyBlockIterator(operation, mlirRegionGetFirstBlock(region));
241}
242
244 operation->checkValid();
245 intptr_t count = 0;
246 MlirBlock block = mlirRegionGetFirstBlock(region);
247 while (!mlirBlockIsNull(block)) {
248 count += 1;
249 block = mlirBlockGetNextInRegion(block);
250 }
251 return count;
252}
253
255 operation->checkValid();
256 if (index < 0) {
257 index += dunderLen();
258 }
259 if (index < 0) {
260 throw nb::index_error("attempt to access out of bounds block");
261 }
262 MlirBlock block = mlirRegionGetFirstBlock(region);
263 while (!mlirBlockIsNull(block)) {
264 if (index == 0) {
265 return PyBlock(operation, block);
266 }
267 block = mlirBlockGetNextInRegion(block);
268 index -= 1;
269 }
270 throw nb::index_error("attempt to access out of bounds block");
271}
272
273PyBlock PyBlockList::appendBlock(const nb::args &pyArgTypes,
274 const std::optional<nb::sequence> &pyArgLocs) {
275 operation->checkValid();
276 MlirBlock block = createBlock(nb::cast<nb::sequence>(pyArgTypes), pyArgLocs);
277 mlirRegionAppendOwnedBlock(region, block);
278 return PyBlock(operation, block);
279}
280
281void PyBlockList::bind(nb::module_ &m) {
282 nb::class_<PyBlockList>(m, "BlockList")
283 .def("__getitem__", &PyBlockList::dunderGetItem,
284 "Returns the block at the specified index.")
285 .def("__iter__", &PyBlockList::dunderIter,
286 "Returns an iterator over blocks in the operation's region.")
287 .def("__len__", &PyBlockList::dunderLen,
288 "Returns the number of blocks in the operation's region.")
289 .def("append", &PyBlockList::appendBlock,
290 R"(
291 Appends a new block, with argument types as positional args.
292
293 Returns:
294 The created block.
295 )",
296 "args"_a, nb::kw_only(), "arg_locs"_a = std::nullopt);
297}
298
299nb::typed<nb::object, PyOpView> PyOperationIterator::dunderNext() {
300 parentOperation->checkValid();
301 if (mlirOperationIsNull(next)) {
302 PyErr_SetNone(PyExc_StopIteration);
303 // python functions should return NULL after setting any exception
304 return nb::object();
305 }
306
307 PyOperationRef returnOperation =
308 PyOperation::forOperation(parentOperation->getContext(), next);
309 next = mlirOperationGetNextInBlock(next);
310 return returnOperation->createOpView();
311}
312
313void PyOperationIterator::bind(nb::module_ &m) {
314 nb::class_<PyOperationIterator>(m, "OperationIterator")
315 .def("__iter__", &PyOperationIterator::dunderIter,
316 "Returns an iterator over the operations in an operation's block.")
317 .def("__next__", &PyOperationIterator::dunderNext,
318 "Returns the next operation in the iteration.");
319}
320
322 parentOperation->checkValid();
323 return PyOperationIterator(parentOperation,
325}
326
328 parentOperation->checkValid();
329 intptr_t count = 0;
330 MlirOperation childOp = mlirBlockGetFirstOperation(block);
331 while (!mlirOperationIsNull(childOp)) {
332 count += 1;
333 childOp = mlirOperationGetNextInBlock(childOp);
334 }
335 return count;
336}
337
338nb::typed<nb::object, PyOpView> PyOperationList::dunderGetItem(intptr_t index) {
339 parentOperation->checkValid();
340 if (index < 0) {
341 index += dunderLen();
342 }
343 if (index < 0) {
344 throw nb::index_error("attempt to access out of bounds operation");
345 }
346 MlirOperation childOp = mlirBlockGetFirstOperation(block);
347 while (!mlirOperationIsNull(childOp)) {
348 if (index == 0) {
349 return PyOperation::forOperation(parentOperation->getContext(), childOp)
350 ->createOpView();
351 }
352 childOp = mlirOperationGetNextInBlock(childOp);
353 index -= 1;
354 }
355 throw nb::index_error("attempt to access out of bounds operation");
356}
357
358void PyOperationList::bind(nb::module_ &m) {
359 nb::class_<PyOperationList>(m, "OperationList")
360 .def("__getitem__", &PyOperationList::dunderGetItem,
361 "Returns the operation at the specified index.")
362 .def("__iter__", &PyOperationList::dunderIter,
363 "Returns an iterator over operations in the list.")
364 .def("__len__", &PyOperationList::dunderLen,
365 "Returns the number of operations in the list.");
366}
367
368nb::typed<nb::object, PyOpView> PyOpOperand::getOwner() const {
369 MlirOperation owner = mlirOpOperandGetOwner(opOperand);
373}
375size_t PyOpOperand::getOperandNumber() const {
376 return mlirOpOperandGetOperandNumber(opOperand);
377}
378
379void PyOpOperand::bind(nb::module_ &m) {
380 nb::class_<PyOpOperand>(m, "OpOperand")
381 .def_prop_ro("owner", &PyOpOperand::getOwner,
382 "Returns the operation that owns this operand.")
383 .def_prop_ro("operand_number", &PyOpOperand::getOperandNumber,
384 "Returns the operand number in the owning operation.");
385}
386
387nb::typed<nb::object, PyOpOperand> PyOpOperandIterator::dunderNext() {
388 if (mlirOpOperandIsNull(opOperand)) {
389 PyErr_SetNone(PyExc_StopIteration);
390 // python functions should return NULL after setting any exception
391 return nb::object();
392 }
393
394 PyOpOperand returnOpOperand(opOperand);
395 opOperand = mlirOpOperandGetNextUse(opOperand);
396 return nb::cast(returnOpOperand);
397}
398
399void PyOpOperandIterator::bind(nb::module_ &m) {
400 nb::class_<PyOpOperandIterator>(m, "OpOperandIterator")
401 .def("__iter__", &PyOpOperandIterator::dunderIter,
402 "Returns an iterator over operands.")
403 .def("__next__", &PyOpOperandIterator::dunderNext,
404 "Returns the next operand in the iteration.");
405}
407//------------------------------------------------------------------------------
408// PyThreadPool
409//------------------------------------------------------------------------------
410
412
414 if (threadPool.ptr)
415 mlirLlvmThreadPoolDestroy(threadPool);
416}
419 return mlirLlvmThreadPoolGetMaxConcurrency(threadPool);
420}
421
422std::string PyThreadPool::_mlir_thread_pool_ptr() const {
423 std::stringstream ss;
424 ss << threadPool.ptr;
425 return ss.str();
426}
428//------------------------------------------------------------------------------
429// PyMlirContext
430//------------------------------------------------------------------------------
431
432PyMlirContext::PyMlirContext(MlirContext context) : context(context) {
433 nb::gil_scoped_acquire acquire;
434 nb::ft_lock_guard lock(live_contexts_mutex);
435 auto &liveContexts = getLiveContexts();
436 liveContexts[context.ptr] = this;
437}
438
440 // Note that the only public way to construct an instance is via the
441 // forContext method, which always puts the associated handle into
442 // liveContexts.
443 nb::gil_scoped_acquire acquire;
444 {
445 nb::ft_lock_guard lock(live_contexts_mutex);
446 getLiveContexts().erase(context.ptr);
447 }
448 mlirContextDestroy(context);
449}
452 return PyMlirContextRef(this, nb::cast(this));
453}
455nb::object PyMlirContext::getCapsule() {
456 return nb::steal<nb::object>(mlirPythonContextToCapsule(get()));
457}
458
459nb::object PyMlirContext::createFromCapsule(nb::object capsule) {
460 MlirContext rawContext = mlirPythonCapsuleToContext(capsule.ptr());
461 if (mlirContextIsNull(rawContext))
462 throw nb::python_error();
463 return forContext(rawContext).releaseObject();
464}
465
466PyMlirContextRef PyMlirContext::forContext(MlirContext context) {
467 nb::gil_scoped_acquire acquire;
468 nb::ft_lock_guard lock(live_contexts_mutex);
469 auto &liveContexts = getLiveContexts();
470 auto it = liveContexts.find(context.ptr);
471 if (it == liveContexts.end()) {
472 // Create.
473 PyMlirContext *unownedContextWrapper = new PyMlirContext(context);
474 nb::object pyRef = nb::cast(unownedContextWrapper);
475 assert(pyRef && "cast to nb::object failed");
476 liveContexts[context.ptr] = unownedContextWrapper;
477 return PyMlirContextRef(unownedContextWrapper, std::move(pyRef));
478 }
479 // Use existing.
480 nb::object pyRef = nb::cast(it->second);
481 return PyMlirContextRef(it->second, std::move(pyRef));
482}
483
484nb::ft_mutex PyMlirContext::live_contexts_mutex;
485
486PyMlirContext::LiveContextMap &PyMlirContext::getLiveContexts() {
487 static LiveContextMap liveContexts;
488 return liveContexts;
489}
490
492 nb::ft_lock_guard lock(live_contexts_mutex);
493 return getLiveContexts().size();
494}
496nb::object PyMlirContext::contextEnter(nb::object context) {
497 return PyThreadContextEntry::pushContext(context);
498}
499
500void PyMlirContext::contextExit(const nb::object &excType,
501 const nb::object &excVal,
502 const nb::object &excTb) {
504}
505
506nb::object PyMlirContext::attachDiagnosticHandler(nb::object callback) {
507 // Note that ownership is transferred to the delete callback below by way of
508 // an explicit inc_ref (borrow).
509 PyDiagnosticHandler *pyHandler =
510 new PyDiagnosticHandler(get(), std::move(callback));
511 nb::object pyHandlerObject =
512 nb::cast(pyHandler, nb::rv_policy::take_ownership);
513 (void)pyHandlerObject.inc_ref();
514
515 // In these C callbacks, the userData is a PyDiagnosticHandler* that is
516 // guaranteed to be known to pybind.
517 auto handlerCallback =
518 +[](MlirDiagnostic diagnostic, void *userData) -> MlirLogicalResult {
519 PyDiagnostic *pyDiagnostic = new PyDiagnostic(diagnostic);
520 nb::object pyDiagnosticObject =
521 nb::cast(pyDiagnostic, nb::rv_policy::take_ownership);
522
523 auto *pyHandler = static_cast<PyDiagnosticHandler *>(userData);
524 bool result = false;
525 {
526 // Since this can be called from arbitrary C++ contexts, always get the
527 // gil.
528 nb::gil_scoped_acquire gil;
529 try {
530 result = nb::cast<bool>(pyHandler->callback(pyDiagnostic));
531 } catch (std::exception &e) {
532 fprintf(stderr, "MLIR Python Diagnostic handler raised exception: %s\n",
533 e.what());
534 pyHandler->hadError = true;
535 }
536 }
537
538 pyDiagnostic->invalidate();
540 };
541 auto deleteCallback = +[](void *userData) {
542 auto *pyHandler = static_cast<PyDiagnosticHandler *>(userData);
543 assert(pyHandler->registeredID && "handler is not registered");
544 pyHandler->registeredID.reset();
545
546 // Decrement reference, balancing the inc_ref() above.
547 nb::object pyHandlerObject = nb::cast(pyHandler, nb::rv_policy::reference);
548 pyHandlerObject.dec_ref();
549 };
550
551 pyHandler->registeredID = mlirContextAttachDiagnosticHandler(
552 get(), handlerCallback, static_cast<void *>(pyHandler), deleteCallback);
553 return pyHandlerObject;
554}
555
556MlirLogicalResult PyMlirContext::ErrorCapture::handler(MlirDiagnostic diag,
557 void *userData) {
558 auto *self = static_cast<ErrorCapture *>(userData);
559 // Check if the context requested we emit errors instead of capturing them.
560 if (self->ctx->emitErrorDiagnostics)
562
564 MlirDiagnosticSeverity::MlirDiagnosticError)
567 self->errors.emplace_back(PyDiagnostic(diag).getInfo());
569}
570
573 if (!context) {
574 throw std::runtime_error(
575 "An MLIR function requires a Context but none was provided in the call "
576 "or from the surrounding environment. Either pass to the function with "
577 "a 'context=' argument or establish a default using 'with Context():'");
578 }
579 return *context;
580}
582//------------------------------------------------------------------------------
583// PyThreadContextEntry management
584//------------------------------------------------------------------------------
585
586std::vector<PyThreadContextEntry> &PyThreadContextEntry::getStack() {
587 static thread_local std::vector<PyThreadContextEntry> stack;
588 return stack;
589}
590
592 auto &stack = getStack();
593 if (stack.empty())
594 return nullptr;
595 return &stack.back();
596}
597
598void PyThreadContextEntry::push(FrameKind frameKind, nb::object context,
599 nb::object insertionPoint,
600 nb::object location) {
601 auto &stack = getStack();
602 stack.emplace_back(frameKind, std::move(context), std::move(insertionPoint),
603 std::move(location));
604 // If the new stack has more than one entry and the context of the new top
605 // entry matches the previous, copy the insertionPoint and location from the
606 // previous entry if missing from the new top entry.
607 if (stack.size() > 1) {
608 auto &prev = *(stack.rbegin() + 1);
609 auto &current = stack.back();
610 if (current.context.is(prev.context)) {
611 // Default non-context objects from the previous entry.
612 if (!current.insertionPoint)
613 current.insertionPoint = prev.insertionPoint;
614 if (!current.location)
615 current.location = prev.location;
616 }
617 }
618}
619
621 if (!context)
622 return nullptr;
623 return nb::cast<PyMlirContext *>(context);
624}
625
627 if (!insertionPoint)
628 return nullptr;
629 return nb::cast<PyInsertionPoint *>(insertionPoint);
630}
631
633 if (!location)
634 return nullptr;
635 return nb::cast<PyLocation *>(location);
636}
637
639 auto *tos = getTopOfStack();
640 return tos ? tos->getContext() : nullptr;
641}
642
644 auto *tos = getTopOfStack();
645 return tos ? tos->getInsertionPoint() : nullptr;
646}
647
649 auto *tos = getTopOfStack();
650 return tos ? tos->getLocation() : nullptr;
651}
652
653nb::object PyThreadContextEntry::pushContext(nb::object context) {
654 push(FrameKind::Context, /*context=*/context,
655 /*insertionPoint=*/nb::object(),
656 /*location=*/nb::object());
657 return context;
658}
659
661 auto &stack = getStack();
662 if (stack.empty())
663 throw std::runtime_error("Unbalanced Context enter/exit");
664 auto &tos = stack.back();
665 if (tos.frameKind != FrameKind::Context && tos.getContext() != &context)
666 throw std::runtime_error("Unbalanced Context enter/exit");
667 stack.pop_back();
668}
669
670nb::object
671PyThreadContextEntry::pushInsertionPoint(nb::object insertionPointObj) {
672 PyInsertionPoint &insertionPoint =
673 nb::cast<PyInsertionPoint &>(insertionPointObj);
674 nb::object contextObj =
675 insertionPoint.getBlock().getParentOperation()->getContext().getObject();
676 push(FrameKind::InsertionPoint,
677 /*context=*/contextObj,
678 /*insertionPoint=*/insertionPointObj,
679 /*location=*/nb::object());
680 return insertionPointObj;
681}
682
684 auto &stack = getStack();
685 if (stack.empty())
686 throw std::runtime_error("Unbalanced InsertionPoint enter/exit");
687 auto &tos = stack.back();
688 if (tos.frameKind != FrameKind::InsertionPoint &&
689 tos.getInsertionPoint() != &insertionPoint)
690 throw std::runtime_error("Unbalanced InsertionPoint enter/exit");
691 stack.pop_back();
692}
693
694nb::object PyThreadContextEntry::pushLocation(nb::object locationObj) {
695 PyLocation &location = nb::cast<PyLocation &>(locationObj);
696 nb::object contextObj = location.getContext().getObject();
697 push(FrameKind::Location, /*context=*/contextObj,
698 /*insertionPoint=*/nb::object(),
699 /*location=*/locationObj);
700 return locationObj;
701}
702
704 auto &stack = getStack();
705 if (stack.empty())
706 throw std::runtime_error("Unbalanced Location enter/exit");
707 auto &tos = stack.back();
708 if (tos.frameKind != FrameKind::Location && tos.getLocation() != &location)
709 throw std::runtime_error("Unbalanced Location enter/exit");
710 stack.pop_back();
711}
713//------------------------------------------------------------------------------
714// PyDiagnostic*
715//------------------------------------------------------------------------------
716
718 valid = false;
719 if (materializedNotes) {
720 for (nb::handle noteObject : *materializedNotes) {
721 PyDiagnostic *note = nb::cast<PyDiagnostic *>(noteObject);
722 note->invalidate();
723 }
724 }
725}
726
728 nb::object callback)
729 : context(context), callback(std::move(callback)) {}
730
732
734 if (!registeredID)
735 return;
736 MlirDiagnosticHandlerID localID = *registeredID;
737 mlirContextDetachDiagnosticHandler(context, localID);
738 assert(!registeredID && "should have unregistered");
739 // Not strictly necessary but keeps stale pointers from being around to cause
740 // issues.
741 context = {nullptr};
742}
743
744void PyDiagnostic::checkValid() {
745 if (!valid) {
746 throw std::invalid_argument(
747 "Diagnostic is invalid (used outside of callback)");
748 }
749}
750
752 checkValid();
753 return static_cast<PyDiagnosticSeverity>(
754 mlirDiagnosticGetSeverity(diagnostic));
755}
756
757nb::typed<nb::object, PyLocation> PyDiagnostic::getLocation() {
758 checkValid();
759 MlirLocation loc = mlirDiagnosticGetLocation(diagnostic);
760 MlirContext context = mlirLocationGetContext(loc);
761 return PyLocation(PyMlirContext::forContext(context), loc).maybeDownCast();
762}
763
764nb::str PyDiagnostic::getMessage() {
765 checkValid();
766 nb::object fileObject = nb::module_::import_("io").attr("StringIO")();
767 PyFileAccumulator accum(fileObject, /*binary=*/false);
768 mlirDiagnosticPrint(diagnostic, accum.getCallback(), accum.getUserData());
769 return nb::cast<nb::str>(fileObject.attr("getvalue")());
770}
771
772nb::typed<nb::tuple, PyDiagnostic> PyDiagnostic::getNotes() {
773 checkValid();
774 if (materializedNotes)
775 return *materializedNotes;
776 intptr_t numNotes = mlirDiagnosticGetNumNotes(diagnostic);
777 nb::tuple notes = nb::steal<nb::tuple>(PyTuple_New(numNotes));
778 for (intptr_t i = 0; i < numNotes; ++i) {
779 MlirDiagnostic noteDiag = mlirDiagnosticGetNote(diagnostic, i);
780 nb::object diagnostic = nb::cast(PyDiagnostic(noteDiag));
781 PyTuple_SetItem(notes.ptr(), i, diagnostic.release().ptr());
782 }
783 materializedNotes = std::move(notes);
784
785 return *materializedNotes;
786}
787
789 std::vector<DiagnosticInfo> notes;
790 for (nb::handle n : getNotes())
791 notes.emplace_back(nb::cast<PyDiagnostic>(n).getInfo());
792 return {getSeverity(), nb::cast<PyLocation>(getLocation()),
793 nb::cast<std::string>(getMessage()), std::move(notes)};
794}
796//------------------------------------------------------------------------------
797// PyDialect, PyDialectDescriptor, PyDialects, PyDialectRegistry
798//------------------------------------------------------------------------------
799
800MlirDialect PyDialects::getDialectForKey(const std::string &key,
801 bool attrError) {
802 MlirDialect dialect = mlirContextGetOrLoadDialect(getContext()->get(),
803 {key.data(), key.size()});
804 if (mlirDialectIsNull(dialect)) {
805 std::string msg = join("Dialect '", key, "' not found");
806 if (attrError)
807 throw nb::attribute_error(msg.c_str());
808 throw nb::index_error(msg.c_str());
809 }
810 return dialect;
811}
814 return nb::steal<nb::object>(mlirPythonDialectRegistryToCapsule(*this));
815}
816
818 MlirDialectRegistry rawRegistry =
820 if (mlirDialectRegistryIsNull(rawRegistry))
821 throw nb::python_error();
822 return PyDialectRegistry(rawRegistry);
823}
825//------------------------------------------------------------------------------
826// PyLocation
827//------------------------------------------------------------------------------
829nb::object PyLocation::getCapsule() {
830 return nb::steal<nb::object>(mlirPythonLocationToCapsule(*this));
831}
832
833PyLocation PyLocation::createFromCapsule(nb::object capsule) {
834 MlirLocation rawLoc = mlirPythonCapsuleToLocation(capsule.ptr());
835 if (mlirLocationIsNull(rawLoc))
836 throw nb::python_error();
838 rawLoc);
839}
841nb::object PyLocation::contextEnter(nb::object locationObj) {
842 return PyThreadContextEntry::pushLocation(locationObj);
843}
844
845void PyLocation::contextExit(const nb::object &excType,
846 const nb::object &excVal,
847 const nb::object &excTb) {
849}
850
853 if (!location) {
854 throw std::runtime_error(
855 "An MLIR function requires a Location but none was provided in the "
856 "call or from the surrounding environment. Either pass to the function "
857 "with a 'loc=' argument or establish a default using 'with loc:'");
858 }
859 return *location;
860}
861
862//------------------------------------------------------------------------------
863// PyModule
864//------------------------------------------------------------------------------
865
866PyModule::PyModule(PyMlirContextRef contextRef, MlirModule module)
867 : BaseContextObject(std::move(contextRef)), module(module) {}
868
870 nb::gil_scoped_acquire acquire;
871 auto &liveModules = getContext()->liveModules;
872 assert(liveModules.count(module.ptr) == 1 &&
873 "destroying module not in live map");
874 liveModules.erase(module.ptr);
875 mlirModuleDestroy(module);
876}
877
878PyModuleRef PyModule::forModule(MlirModule module) {
879 MlirContext context = mlirModuleGetContext(module);
880 PyMlirContextRef contextRef = PyMlirContext::forContext(context);
881
882 nb::gil_scoped_acquire acquire;
883 auto &liveModules = contextRef->liveModules;
884 auto it = liveModules.find(module.ptr);
885 if (it == liveModules.end()) {
886 // Create.
887 PyModule *unownedModule = new PyModule(std::move(contextRef), module);
888 // Note that the default return value policy on cast is automatic_reference,
889 // which does not take ownership (delete will not be called).
890 // Just be explicit.
891 nb::object pyRef = nb::cast(unownedModule, nb::rv_policy::take_ownership);
892 unownedModule->handle = pyRef;
893 liveModules[module.ptr] =
894 std::make_pair(unownedModule->handle, unownedModule);
895 return PyModuleRef(unownedModule, std::move(pyRef));
896 }
897 // Use existing.
898 PyModule *existing = it->second.second;
899 nb::object pyRef = nb::borrow<nb::object>(it->second.first);
900 return PyModuleRef(existing, std::move(pyRef));
901}
902
903nb::object PyModule::createFromCapsule(nb::object capsule) {
904 MlirModule rawModule = mlirPythonCapsuleToModule(capsule.ptr());
905 if (mlirModuleIsNull(rawModule))
906 throw nb::python_error();
907 return forModule(rawModule).releaseObject();
908}
909
910nb::object PyModule::getCapsule() {
911 return nb::steal<nb::object>(mlirPythonModuleToCapsule(get()));
912}
914//------------------------------------------------------------------------------
915// PyOperation
916//------------------------------------------------------------------------------
917
918PyOperation::PyOperation(PyMlirContextRef contextRef, MlirOperation operation)
919 : BaseContextObject(std::move(contextRef)), operation(operation) {}
920
922 // If the operation has already been invalidated there is nothing to do.
923 if (!valid)
924 return;
925 // Otherwise, invalidate the operation when it is attached.
926 if (isAttached())
927 setInvalid();
928 else {
929 // And destroy it when it is detached, i.e. owned by Python.
930 erase();
931 }
932}
933
934namespace {
935
936// Constructs a new object of type T in-place on the Python heap, returning a
937// PyObjectRef to it, loosely analogous to std::make_shared<T>().
938template <typename T, class... Args>
939PyObjectRef<T> makeObjectRef(Args &&...args) {
940 nb::handle type = nb::type<T>();
941 nb::object instance = nb::inst_alloc(type);
942 T *ptr = nb::inst_ptr<T>(instance);
943 new (ptr) T(std::forward<Args>(args)...);
944 nb::inst_mark_ready(instance);
945 return PyObjectRef<T>(ptr, std::move(instance));
946}
947
948} // namespace
949
950PyOperationRef PyOperation::createInstance(PyMlirContextRef contextRef,
951 MlirOperation operation,
952 nb::object parentKeepAlive) {
953 // Create.
954 PyOperationRef unownedOperation =
955 makeObjectRef<PyOperation>(std::move(contextRef), operation);
956 unownedOperation->handle = unownedOperation.getObject();
957 if (parentKeepAlive) {
958 unownedOperation->parentKeepAlive = std::move(parentKeepAlive);
959 }
960 return unownedOperation;
961}
962
964 MlirOperation operation,
965 nb::object parentKeepAlive) {
966 return createInstance(std::move(contextRef), operation,
967 std::move(parentKeepAlive));
968}
969
971 MlirOperation operation,
972 nb::object parentKeepAlive) {
973 PyOperationRef created = createInstance(std::move(contextRef), operation,
974 std::move(parentKeepAlive));
975 created->attached = false;
976 return created;
977}
978
980 const std::string &sourceStr,
981 const std::string &sourceName) {
982 PyMlirContext::ErrorCapture errors(contextRef);
983 MlirOperation op =
984 mlirOperationCreateParse(contextRef->get(), toMlirStringRef(sourceStr),
985 toMlirStringRef(sourceName));
986 if (mlirOperationIsNull(op))
987 throw MLIRError("Unable to parse operation assembly", errors.take());
988 return PyOperation::createDetached(std::move(contextRef), op);
989}
990
993 setDetached();
994 parentKeepAlive = nb::object();
995}
996
997MlirOperation PyOperation::get() const {
998 checkValid();
999 return operation;
1000}
1003 return PyOperationRef(this, nb::borrow<nb::object>(handle));
1004}
1005
1006void PyOperation::setAttached(const nb::object &parent) {
1007 assert(!attached && "operation already attached");
1008 attached = true;
1009}
1010
1012 assert(attached && "operation already detached");
1013 attached = false;
1014}
1015
1016void PyOperation::checkValid() const {
1017 if (!valid) {
1018 throw std::runtime_error("the operation has been invalidated");
1019 }
1020}
1021
1022void PyOperationBase::print(std::optional<int64_t> largeElementsLimit,
1023 std::optional<int64_t> largeResourceLimit,
1024 bool enableDebugInfo, bool prettyDebugInfo,
1025 bool printGenericOpForm, bool useLocalScope,
1026 bool useNameLocAsPrefix, bool assumeVerified,
1027 nb::object fileObject, bool binary,
1028 bool skipRegions) {
1029 PyOperation &operation = getOperation();
1030 operation.checkValid();
1031 if (fileObject.is_none())
1032 fileObject = nb::module_::import_("sys").attr("stdout");
1033
1034 MlirOpPrintingFlags flags = mlirOpPrintingFlagsCreate();
1035 if (largeElementsLimit)
1036 mlirOpPrintingFlagsElideLargeElementsAttrs(flags, *largeElementsLimit);
1037 if (largeResourceLimit)
1038 mlirOpPrintingFlagsElideLargeResourceString(flags, *largeResourceLimit);
1039 if (enableDebugInfo)
1040 mlirOpPrintingFlagsEnableDebugInfo(flags, /*enable=*/true,
1041 /*prettyForm=*/prettyDebugInfo);
1042 if (printGenericOpForm)
1044 if (useLocalScope)
1046 if (assumeVerified)
1048 if (skipRegions)
1050 if (useNameLocAsPrefix)
1052
1053 PyFileAccumulator accum(fileObject, binary);
1054 mlirOperationPrintWithFlags(operation, flags, accum.getCallback(),
1055 accum.getUserData());
1057}
1058
1059void PyOperationBase::print(PyAsmState &state, nb::object fileObject,
1060 bool binary) {
1061 PyOperation &operation = getOperation();
1062 operation.checkValid();
1063 if (fileObject.is_none())
1064 fileObject = nb::module_::import_("sys").attr("stdout");
1065 PyFileAccumulator accum(fileObject, binary);
1066 mlirOperationPrintWithState(operation, state.get(), accum.getCallback(),
1067 accum.getUserData());
1068}
1069
1070void PyOperationBase::writeBytecode(const nb::object &fileOrStringObject,
1071 std::optional<int64_t> bytecodeVersion) {
1072 PyOperation &operation = getOperation();
1073 operation.checkValid();
1074 PyFileAccumulator accum(fileOrStringObject, /*binary=*/true);
1075
1076 if (!bytecodeVersion.has_value())
1077 return mlirOperationWriteBytecode(operation, accum.getCallback(),
1078 accum.getUserData());
1079
1080 MlirBytecodeWriterConfig config = mlirBytecodeWriterConfigCreate();
1081 mlirBytecodeWriterConfigDesiredEmitVersion(config, *bytecodeVersion);
1083 operation, config, accum.getCallback(), accum.getUserData());
1086 throw nb::value_error(
1087 join("Unable to honor desired bytecode version ", *bytecodeVersion)
1088 .c_str());
1089}
1090
1091void PyOperationBase::walk(std::function<PyWalkResult(MlirOperation)> callback,
1092 PyWalkOrder walkOrder) {
1093 PyOperation &operation = getOperation();
1094 operation.checkValid();
1095 struct UserData {
1096 std::function<PyWalkResult(MlirOperation)> callback;
1097 bool gotException;
1098 std::string exceptionWhat;
1099 nb::object exceptionType;
1100 };
1101 UserData userData{callback, false, {}, {}};
1102 MlirOperationWalkCallback walkCallback = [](MlirOperation op,
1103 void *userData) {
1104 UserData *calleeUserData = static_cast<UserData *>(userData);
1105 try {
1106 return static_cast<MlirWalkResult>((calleeUserData->callback)(op));
1107 } catch (nb::python_error &e) {
1108 calleeUserData->gotException = true;
1109 calleeUserData->exceptionWhat = std::string(e.what());
1110 calleeUserData->exceptionType = nb::borrow(e.type());
1111 return MlirWalkResult::MlirWalkResultInterrupt;
1112 }
1113 };
1114 mlirOperationWalk(operation, walkCallback, &userData,
1115 static_cast<MlirWalkOrder>(walkOrder));
1116 if (userData.gotException) {
1117 std::string message("Exception raised in callback: ");
1118 message.append(userData.exceptionWhat);
1119 throw std::runtime_error(message);
1120 }
1121}
1122
1123nb::object PyOperationBase::getAsm(bool binary,
1124 std::optional<int64_t> largeElementsLimit,
1125 std::optional<int64_t> largeResourceLimit,
1126 bool enableDebugInfo, bool prettyDebugInfo,
1127 bool printGenericOpForm, bool useLocalScope,
1128 bool useNameLocAsPrefix, bool assumeVerified,
1129 bool skipRegions) {
1130 nb::object fileObject;
1131 if (binary) {
1132 fileObject = nb::module_::import_("io").attr("BytesIO")();
1133 } else {
1134 fileObject = nb::module_::import_("io").attr("StringIO")();
1135 }
1136 print(/*largeElementsLimit=*/largeElementsLimit,
1137 /*largeResourceLimit=*/largeResourceLimit,
1138 /*enableDebugInfo=*/enableDebugInfo,
1139 /*prettyDebugInfo=*/prettyDebugInfo,
1140 /*printGenericOpForm=*/printGenericOpForm,
1141 /*useLocalScope=*/useLocalScope,
1142 /*useNameLocAsPrefix=*/useNameLocAsPrefix,
1143 /*assumeVerified=*/assumeVerified,
1144 /*fileObject=*/fileObject,
1145 /*binary=*/binary,
1146 /*skipRegions=*/skipRegions);
1147
1148 return fileObject.attr("getvalue")();
1149}
1150
1152 PyOperation &operation = getOperation();
1153 PyOperation &otherOp = other.getOperation();
1154 operation.checkValid();
1155 otherOp.checkValid();
1156 mlirOperationMoveAfter(operation, otherOp);
1157 operation.parentKeepAlive = otherOp.parentKeepAlive;
1158}
1159
1161 PyOperation &operation = getOperation();
1162 PyOperation &otherOp = other.getOperation();
1163 operation.checkValid();
1164 otherOp.checkValid();
1165 mlirOperationMoveBefore(operation, otherOp);
1166 operation.parentKeepAlive = otherOp.parentKeepAlive;
1167}
1168
1170 PyOperation &operation = getOperation();
1171 PyOperation &otherOp = other.getOperation();
1172 operation.checkValid();
1173 otherOp.checkValid();
1174 return mlirOperationIsBeforeInBlock(operation, otherOp);
1175}
1176
1178 PyOperation &op = getOperation();
1181 throw MLIRError("Verification failed", errors.take());
1182 return true;
1183}
1184
1185std::optional<PyOperationRef> PyOperation::getParentOperation() {
1186 checkValid();
1187 if (!isAttached())
1188 throw nb::value_error("Detached operations have no parent");
1189 MlirOperation operation = mlirOperationGetParentOperation(get());
1190 if (mlirOperationIsNull(operation))
1191 return {};
1192 return PyOperation::forOperation(getContext(), operation);
1193}
1194
1196 checkValid();
1197 std::optional<PyOperationRef> parentOperation = getParentOperation();
1198 MlirBlock block = mlirOperationGetBlock(get());
1199 assert(!mlirBlockIsNull(block) && "Attached operation has null parent");
1200 assert(parentOperation && "Operation has no parent");
1201 return PyBlock{std::move(*parentOperation), block};
1202}
1203
1205 checkValid();
1206 return nb::steal<nb::object>(mlirPythonOperationToCapsule(get()));
1207}
1208
1209nb::object PyOperation::createFromCapsule(const nb::object &capsule) {
1210 MlirOperation rawOperation = mlirPythonCapsuleToOperation(capsule.ptr());
1211 if (mlirOperationIsNull(rawOperation))
1212 throw nb::python_error();
1213 MlirContext rawCtxt = mlirOperationGetContext(rawOperation);
1214 return forOperation(PyMlirContext::forContext(rawCtxt), rawOperation)
1215 .releaseObject();
1216}
1217
1218static void maybeInsertOperation(PyOperationRef &op,
1219 const nb::object &maybeIp) {
1220 // InsertPoint active?
1221 if (!maybeIp.is(nb::cast(false))) {
1222 PyInsertionPoint *ip;
1223 if (maybeIp.is_none()) {
1225 } else {
1226 ip = nb::cast<PyInsertionPoint *>(maybeIp);
1227 }
1228 if (ip)
1229 ip->insert(*op.get());
1230 }
1231}
1232
1233nb::object PyOperation::create(std::string_view name,
1234 std::optional<std::vector<PyType *>> results,
1235 const MlirValue *operands, size_t numOperands,
1236 std::optional<nb::dict> attributes,
1237 std::optional<std::vector<PyBlock *>> successors,
1238 int regions, PyLocation &location,
1239 const nb::object &maybeIp, bool inferType) {
1240 std::vector<MlirType> mlirResults;
1241 std::vector<MlirBlock> mlirSuccessors;
1242 std::vector<std::pair<std::string, MlirAttribute>> mlirAttributes;
1243
1244 // General parameter validation.
1245 if (regions < 0)
1246 throw nb::value_error("number of regions must be >= 0");
1247
1248 // Unpack/validate results.
1249 if (results) {
1250 mlirResults.reserve(results->size());
1251 for (PyType *result : *results) {
1252 // TODO: Verify result type originate from the same context.
1253 if (!result)
1254 throw nb::value_error("result type cannot be None");
1255 mlirResults.push_back(*result);
1256 }
1257 }
1258 // Unpack/validate attributes.
1259 if (attributes) {
1260 mlirAttributes.reserve(attributes->size());
1261 for (std::pair<nb::handle, nb::handle> it : *attributes) {
1262 std::string key;
1263 try {
1264 key = nb::cast<std::string>(it.first);
1265 } catch (nb::cast_error &err) {
1266 std::string msg = join("Invalid attribute key (not a string) when "
1267 "attempting to create the operation \"",
1268 name, "\" (", err.what(), ")");
1269 throw nb::type_error(msg.c_str());
1270 }
1271 try {
1272 auto &attribute = nb::cast<PyAttribute &>(it.second);
1273 // TODO: Verify attribute originates from the same context.
1274 mlirAttributes.emplace_back(std::move(key), attribute);
1275 } catch (std::exception &err) {
1276 if (it.second.is_none()) {
1277 std::string msg = join(
1278 "Found an invalid (`None`?) attribute value for the key \"", key,
1279 "\" when attempting to create the operation \"", name, "\"");
1280 throw std::runtime_error(msg);
1281 }
1282 std::string msg = join("Invalid attribute value for the key \"", key,
1283 "\" when attempting to create the operation \"",
1284 name, "\" (", err.what(), ")");
1285 throw nb::type_error(msg.c_str());
1286 }
1287 }
1288 }
1289 // Unpack/validate successors.
1290 if (successors) {
1291 mlirSuccessors.reserve(successors->size());
1292 for (PyBlock *successor : *successors) {
1293 // TODO: Verify successor originate from the same context.
1294 if (!successor)
1295 throw nb::value_error("successor block cannot be None");
1296 mlirSuccessors.push_back(successor->get());
1297 }
1298 }
1299
1300 // Apply unpacked/validated to the operation state. Beyond this
1301 // point, exceptions cannot be thrown or else the state will leak.
1302 MlirOperationState state =
1303 mlirOperationStateGet(toMlirStringRef(name), location);
1304 if (numOperands > 0)
1305 mlirOperationStateAddOperands(&state, numOperands, operands);
1306 state.enableResultTypeInference = inferType;
1307 if (!mlirResults.empty())
1308 mlirOperationStateAddResults(&state, mlirResults.size(),
1309 mlirResults.data());
1310 if (!mlirAttributes.empty()) {
1311 // Note that the attribute names directly reference bytes in
1312 // mlirAttributes, so that vector must not be changed from here
1313 // on.
1314 std::vector<MlirNamedAttribute> mlirNamedAttributes;
1315 mlirNamedAttributes.reserve(mlirAttributes.size());
1316 for (const std::pair<std::string, MlirAttribute> &it : mlirAttributes)
1317 mlirNamedAttributes.push_back(mlirNamedAttributeGet(
1319 toMlirStringRef(it.first)),
1320 it.second));
1321 mlirOperationStateAddAttributes(&state, mlirNamedAttributes.size(),
1322 mlirNamedAttributes.data());
1323 }
1324 if (!mlirSuccessors.empty())
1325 mlirOperationStateAddSuccessors(&state, mlirSuccessors.size(),
1326 mlirSuccessors.data());
1327 if (regions) {
1328 std::vector<MlirRegion> mlirRegions;
1329 mlirRegions.resize(regions);
1330 for (int i = 0; i < regions; ++i)
1331 mlirRegions[i] = mlirRegionCreate();
1332 mlirOperationStateAddOwnedRegions(&state, mlirRegions.size(),
1333 mlirRegions.data());
1334 }
1335
1336 // Construct the operation.
1337 PyMlirContext::ErrorCapture errors(location.getContext());
1338 MlirOperation operation = mlirOperationCreate(&state);
1339 if (!operation.ptr)
1340 throw MLIRError("Operation creation failed", errors.take());
1341 PyOperationRef created =
1342 PyOperation::createDetached(location.getContext(), operation);
1343 maybeInsertOperation(created, maybeIp);
1344
1345 return created.getObject();
1346}
1347
1348nb::object PyOperation::clone(const nb::object &maybeIp) {
1349 MlirOperation clonedOperation = mlirOperationClone(operation);
1350 PyOperationRef cloned =
1351 PyOperation::createDetached(getContext(), clonedOperation);
1352 maybeInsertOperation(cloned, maybeIp);
1353
1354 return cloned->createOpView();
1355}
1356
1357nb::object PyOperation::createOpView() {
1358 checkValid();
1359 MlirIdentifier ident = mlirOperationGetName(get());
1360 MlirStringRef identStr = mlirIdentifierStr(ident);
1361 auto operationCls = PyGlobals::get().lookupOperationClass(
1362 std::string_view(identStr.data, identStr.length));
1363 if (operationCls)
1364 return PyOpView::constructDerived(*operationCls, getRef().getObject());
1365 return nb::cast(PyOpView(getRef().getObject()));
1366}
1367
1368void PyOperation::erase() {
1370 setInvalid();
1371 mlirOperationDestroy(operation);
1372}
1373
1374void PyOpResult::bindDerived(ClassTy &c) {
1375 c.def_prop_ro(
1376 "owner",
1377 [](PyOpResult &self) -> nb::typed<nb::object, PyOpView> {
1378 assert(mlirOperationEqual(self.getParentOperation()->get(),
1379 mlirOpResultGetOwner(self.get())) &&
1380 "expected the owner of the value in Python to match that in "
1381 "the IR");
1382 return self.getParentOperation()->createOpView();
1383 },
1384 "Returns the operation that produces this result.");
1385 c.def_prop_ro(
1386 "result_number",
1387 [](PyOpResult &self) { return mlirOpResultGetResultNumber(self.get()); },
1388 "Returns the position of this result in the operation's result list.");
1390
1391/// Returns the list of types of the values held by container.
1392template <typename Container>
1393static std::vector<nb::typed<nb::object, PyType>>
1394getValueTypes(Container &container, PyMlirContextRef &context) {
1395 std::vector<nb::typed<nb::object, PyType>> result;
1396 result.reserve(container.size());
1397 for (int i = 0, e = container.size(); i < e; ++i) {
1398 result.push_back(PyType(context->getRef(),
1399 mlirValueGetType(container.getElement(i).get()))
1401 }
1402 return result;
1403}
1404
1406 intptr_t length, intptr_t step)
1407 : Sliceable(startIndex,
1409 : length,
1410 step),
1411 operation(std::move(operation)) {}
1412
1413void PyOpResultList::bindDerived(ClassTy &c) {
1414 c.def_prop_ro(
1415 "types",
1416 [](PyOpResultList &self) {
1417 return getValueTypes(self, self.operation->getContext());
1418 },
1419 "Returns a list of types for all results in this result list.");
1420 c.def_prop_ro(
1421 "owner",
1422 [](PyOpResultList &self) -> nb::typed<nb::object, PyOpView> {
1423 return self.operation->createOpView();
1424 },
1425 "Returns the operation that owns this result list.");
1426}
1427
1428intptr_t PyOpResultList::getRawNumElements() {
1429 operation->checkValid();
1430 return mlirOperationGetNumResults(operation->get());
1431}
1432
1433PyOpResult PyOpResultList::getRawElement(intptr_t index) {
1434 PyValue value(operation, mlirOperationGetResult(operation->get(), index));
1435 return PyOpResult(value);
1436}
1437
1438PyOpResultList PyOpResultList::slice(intptr_t startIndex, intptr_t length,
1439 intptr_t step) const {
1440 return PyOpResultList(operation, startIndex, length, step);
1441}
1443//------------------------------------------------------------------------------
1444// PyOpView
1445//------------------------------------------------------------------------------
1446
1447static void populateResultTypes(std::string_view name,
1448 nb::sequence resultTypeList,
1449 const nb::object &resultSegmentSpecObj,
1450 std::vector<int32_t> &resultSegmentLengths,
1451 std::vector<PyType *> &resultTypes) {
1452 resultTypes.reserve(nb::len(resultTypeList));
1453 if (resultSegmentSpecObj.is_none()) {
1454 // Non-variadic result unpacking.
1455 size_t index = 0;
1456 for (nb::handle resultType : resultTypeList) {
1457 try {
1458 resultTypes.push_back(nb::cast<PyType *>(resultType));
1459 if (!resultTypes.back())
1460 throw nb::cast_error();
1461 } catch (nb::cast_error &err) {
1462 throw nb::value_error(join("Result ", index, " of operation \"", name,
1463 "\" must be a Type (", err.what(), ")")
1464 .c_str());
1465 }
1466 ++index;
1467 }
1468 } else {
1469 // Sized result unpacking.
1470 auto resultSegmentSpec = nb::cast<std::vector<int>>(resultSegmentSpecObj);
1471 if (resultSegmentSpec.size() != nb::len(resultTypeList)) {
1472 throw nb::value_error(
1473 join("Operation \"", name, "\" requires ", resultSegmentSpec.size(),
1474 " result segments but was provided ", nb::len(resultTypeList))
1475 .c_str());
1476 }
1477 resultSegmentLengths.reserve(nb::len(resultTypeList));
1478 for (size_t i = 0, e = resultSegmentSpec.size(); i < e; ++i) {
1479 int segmentSpec = resultSegmentSpec[i];
1480 if (segmentSpec == 1 || segmentSpec == 0) {
1481 // Unpack unary element.
1482 try {
1483 auto *resultType = nb::cast<PyType *>(resultTypeList[i]);
1484 if (resultType) {
1485 resultTypes.push_back(resultType);
1486 resultSegmentLengths.push_back(1);
1487 } else if (segmentSpec == 0) {
1488 // Allowed to be optional.
1489 resultSegmentLengths.push_back(0);
1490 } else {
1491 throw nb::value_error(
1492 join("Result ", i, " of operation \"", name,
1493 "\" must be a Type (was None and result is not optional)")
1494 .c_str());
1495 }
1496 } catch (nb::cast_error &err) {
1497 throw nb::value_error(join("Result ", i, " of operation \"", name,
1498 "\" must be a Type (", err.what(), ")")
1499 .c_str());
1500 }
1501 } else if (segmentSpec == -1) {
1502 // Unpack sequence by appending.
1503 try {
1504 if (resultTypeList[i].is_none()) {
1505 // Treat it as an empty list.
1506 resultSegmentLengths.push_back(0);
1507 } else {
1508 // Unpack the list.
1509 auto segment = nb::cast<nb::sequence>(resultTypeList[i]);
1510 for (nb::handle segmentItem : segment) {
1511 resultTypes.push_back(nb::cast<PyType *>(segmentItem));
1512 if (!resultTypes.back()) {
1513 throw nb::type_error("contained a None item");
1514 }
1515 }
1516 resultSegmentLengths.push_back(nb::len(segment));
1517 }
1518 } catch (std::exception &err) {
1519 // NOTE: Sloppy to be using a catch-all here, but there are at least
1520 // three different unrelated exceptions that can be thrown in the
1521 // above "casts". Just keep the scope above small and catch them all.
1522 throw nb::value_error(join("Result ", i, " of operation \"", name,
1523 "\" must be a Sequence of Types (",
1524 err.what(), ")")
1525 .c_str());
1526 }
1527 } else {
1528 throw nb::value_error("Unexpected segment spec");
1530 }
1531 }
1532}
1533
1534MlirValue getUniqueResult(MlirOperation operation) {
1535 auto numResults = mlirOperationGetNumResults(operation);
1536 if (numResults != 1) {
1537 auto name = mlirIdentifierStr(mlirOperationGetName(operation));
1538 throw nb::value_error(
1539 join("Cannot call .result on operation ",
1540 std::string_view(name.data, name.length), " which has ",
1541 numResults,
1542 " results (it is only valid for operations with a "
1543 "single result)")
1544 .c_str());
1545 }
1546 return mlirOperationGetResult(operation, 0);
1547}
1548
1549static MlirValue getOpResultOrValue(nb::handle operand) {
1550 if (operand.is_none()) {
1551 throw nb::value_error("contained a None item");
1552 }
1553 PyOperationBase *op;
1554 if (nb::try_cast<PyOperationBase *>(operand, op)) {
1555 return getUniqueResult(op->getOperation());
1556 }
1557 PyOpResultList *opResultList;
1558 if (nb::try_cast<PyOpResultList *>(operand, opResultList)) {
1559 return getUniqueResult(opResultList->getOperation()->get());
1560 }
1561 PyValue *value;
1562 if (nb::try_cast<PyValue *>(operand, value)) {
1563 return value->get();
1564 }
1565 throw nb::value_error("is not a Value");
1566}
1567
1568nb::typed<nb::object, PyOperation> PyOpView::buildGeneric(
1569 std::string_view name, std::tuple<int, bool> opRegionSpec,
1570 nb::object operandSegmentSpecObj, nb::object resultSegmentSpecObj,
1571 std::optional<nb::sequence> resultTypeList, nb::sequence operandList,
1572 std::optional<nb::dict> attributes,
1573 std::optional<std::vector<PyBlock *>> successors,
1574 std::optional<int> regions, PyLocation &location,
1575 const nb::object &maybeIp) {
1576 PyMlirContextRef context = location.getContext();
1577
1578 // Class level operation construction metadata.
1579 // Operand and result segment specs are either none, which does no
1580 // variadic unpacking, or a list of ints with segment sizes, where each
1581 // element is either a positive number (typically 1 for a scalar) or -1 to
1582 // indicate that it is derived from the length of the same-indexed operand
1583 // or result (implying that it is a list at that position).
1584 std::vector<int32_t> operandSegmentLengths;
1585 std::vector<int32_t> resultSegmentLengths;
1586
1587 // Validate/determine region count.
1588 int opMinRegionCount = std::get<0>(opRegionSpec);
1589 bool opHasNoVariadicRegions = std::get<1>(opRegionSpec);
1590 if (!regions) {
1591 regions = opMinRegionCount;
1592 }
1593 if (*regions < opMinRegionCount) {
1594 throw nb::value_error(join("Operation \"", name,
1595 "\" requires a minimum of ", opMinRegionCount,
1596 " regions but was built with regions=", *regions)
1597 .c_str());
1598 }
1599 if (opHasNoVariadicRegions && *regions > opMinRegionCount) {
1600 throw nb::value_error(join("Operation \"", name,
1601 "\" requires a maximum of ", opMinRegionCount,
1602 " regions but was built with regions=", *regions)
1603 .c_str());
1604 }
1605
1606 // Unpack results.
1607 std::vector<PyType *> resultTypes;
1608 if (resultTypeList.has_value()) {
1609 populateResultTypes(name, *resultTypeList, resultSegmentSpecObj,
1610 resultSegmentLengths, resultTypes);
1611 }
1612
1613 // Unpack operands.
1614 std::vector<MlirValue> operands;
1615 operands.reserve(operands.size());
1616 size_t index = 0;
1617 if (operandSegmentSpecObj.is_none()) {
1618 // Non-sized operand unpacking.
1619 for (nb::handle operand : operandList) {
1620 try {
1621 operands.push_back(getOpResultOrValue(operand));
1622 } catch (nb::builtin_exception &err) {
1623 throw nb::value_error(join("Operand ", index, " of operation \"", name,
1624 "\" must be a Value (", err.what(), ")")
1625 .c_str());
1626 }
1627 ++index;
1628 }
1629 } else {
1630 // Sized operand unpacking.
1631 auto operandSegmentSpec = nb::cast<std::vector<int>>(operandSegmentSpecObj);
1632 if (operandSegmentSpec.size() != nb::len(operandList)) {
1633 throw nb::value_error(
1634 join("Operation \"", name, "\" requires ", operandSegmentSpec.size(),
1635 "operand segments but was provided ", nb::len(operandList))
1636 .c_str());
1637 }
1638 operandSegmentLengths.reserve(nb::len(operandList));
1639 for (size_t i = 0, e = operandSegmentSpec.size(); i < e; ++i) {
1640 int segmentSpec = operandSegmentSpec[i];
1641 if (segmentSpec == 1 || segmentSpec == 0) {
1642 // Unpack unary element.
1643 const nanobind::handle operand = operandList[i];
1644 if (!operand.is_none()) {
1645 try {
1646 operands.push_back(getOpResultOrValue(operand));
1647 } catch (nb::builtin_exception &err) {
1648 throw nb::value_error(join("Operand ", i, " of operation \"", name,
1649 "\" must be a Value (", err.what(), ")")
1650 .c_str());
1651 }
1652
1653 operandSegmentLengths.push_back(1);
1654 } else if (segmentSpec == 0) {
1655 // Allowed to be optional.
1656 operandSegmentLengths.push_back(0);
1657 } else {
1658 throw nb::value_error(
1659 join("Operand ", i, " of operation \"", name,
1660 "\" must be a Value (was None and operand is not optional)")
1661 .c_str());
1662 }
1663 } else if (segmentSpec == -1) {
1664 // Unpack sequence by appending.
1665 try {
1666 if (operandList[i].is_none()) {
1667 // Treat it as an empty list.
1668 operandSegmentLengths.push_back(0);
1669 } else {
1670 // Unpack the list.
1671 auto segment = nb::cast<nb::sequence>(operandList[i]);
1672 for (nb::handle segmentItem : segment) {
1673 operands.push_back(getOpResultOrValue(segmentItem));
1674 }
1675 operandSegmentLengths.push_back(nb::len(segment));
1676 }
1677 } catch (std::exception &err) {
1678 // NOTE: Sloppy to be using a catch-all here, but there are at least
1679 // three different unrelated exceptions that can be thrown in the
1680 // above "casts". Just keep the scope above small and catch them all.
1681 throw nb::value_error(join("Operand ", i, " of operation \"", name,
1682 "\" must be a Sequence of Values (",
1683 err.what(), ")")
1684 .c_str());
1685 }
1686 } else {
1687 throw nb::value_error("Unexpected segment spec");
1688 }
1689 }
1690 }
1691
1692 // Merge operand/result segment lengths into attributes if needed.
1693 if (!operandSegmentLengths.empty() || !resultSegmentLengths.empty()) {
1694 // Dup.
1695 if (attributes) {
1696 attributes = nb::dict(*attributes);
1697 } else {
1698 attributes = nb::dict();
1699 }
1700 if (attributes->contains("resultSegmentSizes") ||
1701 attributes->contains("operandSegmentSizes")) {
1702 throw nb::value_error("Manually setting a 'resultSegmentSizes' or "
1703 "'operandSegmentSizes' attribute is unsupported. "
1704 "Use Operation.create for such low-level access.");
1705 }
1706
1707 // Add resultSegmentSizes attribute.
1708 if (!resultSegmentLengths.empty()) {
1709 MlirAttribute segmentLengthAttr =
1710 mlirDenseI32ArrayGet(context->get(), resultSegmentLengths.size(),
1711 resultSegmentLengths.data());
1712 (*attributes)["resultSegmentSizes"] =
1713 PyAttribute(context, segmentLengthAttr);
1714 }
1715
1716 // Add operandSegmentSizes attribute.
1717 if (!operandSegmentLengths.empty()) {
1718 MlirAttribute segmentLengthAttr =
1719 mlirDenseI32ArrayGet(context->get(), operandSegmentLengths.size(),
1720 operandSegmentLengths.data());
1721 (*attributes)["operandSegmentSizes"] =
1722 PyAttribute(context, segmentLengthAttr);
1723 }
1724 }
1725
1726 // Delegate to create.
1727 return PyOperation::create(name,
1728 /*results=*/std::move(resultTypes),
1729 /*operands=*/operands.data(),
1730 /*numOperands=*/operands.size(),
1731 /*attributes=*/std::move(attributes),
1732 /*successors=*/std::move(successors),
1733 /*regions=*/*regions, location, maybeIp,
1734 !resultTypeList);
1735}
1736
1737nb::object PyOpView::constructDerived(const nb::object &cls,
1738 const nb::object &operation) {
1739 nb::handle opViewType = nb::type<PyOpView>();
1740 nb::object instance = cls.attr("__new__")(cls);
1741 opViewType.attr("__init__")(instance, operation);
1742 return instance;
1743}
1744
1745PyOpView::PyOpView(const nb::object &operationObject)
1746 // Casting through the PyOperationBase base-class and then back to the
1747 // Operation lets us accept any PyOperationBase subclass.
1748 : operation(nb::cast<PyOperationBase &>(operationObject).getOperation()),
1749 operationObject(operation.getRef().getObject()) {}
1751//------------------------------------------------------------------------------
1752// PyAsmState
1753//------------------------------------------------------------------------------
1754
1755PyAsmState::PyAsmState(MlirValue value, bool useLocalScope) {
1756 flags = mlirOpPrintingFlagsCreate();
1757 // The OpPrintingFlags are not exposed Python side, create locally and
1758 // associate lifetime with the state.
1759 if (useLocalScope)
1761 state = mlirAsmStateCreateForValue(value, flags);
1762}
1763
1764PyAsmState::PyAsmState(PyOperationBase &operation, bool useLocalScope) {
1765 flags = mlirOpPrintingFlagsCreate();
1766 // The OpPrintingFlags are not exposed Python side, create locally and
1767 // associate lifetime with the state.
1768 if (useLocalScope)
1770 state = mlirAsmStateCreateForOperation(operation.getOperation().get(), flags);
1771}
1773//------------------------------------------------------------------------------
1774// PyInsertionPoint.
1775//------------------------------------------------------------------------------
1776
1777PyInsertionPoint::PyInsertionPoint(const PyBlock &block) : block(block) {}
1780 : refOperation(beforeOperationBase.getOperation().getRef()),
1781 block((*refOperation)->getBlock()) {}
1782
1784 : refOperation(beforeOperationRef), block((*refOperation)->getBlock()) {}
1785
1786void PyInsertionPoint::insert(PyOperationBase &operationBase) {
1787 PyOperation &operation = operationBase.getOperation();
1788 if (operation.isAttached())
1789 throw nb::value_error(
1790 "Attempt to insert operation that is already attached");
1791 block.getParentOperation()->checkValid();
1792 MlirOperation beforeOp = {nullptr};
1793 if (refOperation) {
1794 // Insert before operation.
1795 (*refOperation)->checkValid();
1796 beforeOp = (*refOperation)->get();
1797 } else {
1798 // Insert at end (before null) is only valid if the block does not
1799 // already end in a known terminator (violating this will cause assertion
1800 // failures later).
1801 if (!mlirOperationIsNull(mlirBlockGetTerminator(block.get()))) {
1802 throw nb::index_error("Cannot insert operation at the end of a block "
1803 "that already has a terminator. Did you mean to "
1804 "use 'InsertionPoint.at_block_terminator(block)' "
1805 "versus 'InsertionPoint(block)'?");
1806 }
1808 mlirBlockInsertOwnedOperationBefore(block.get(), beforeOp, operation);
1809 operation.setAttached();
1810}
1811
1813 MlirOperation firstOp = mlirBlockGetFirstOperation(block.get());
1814 if (mlirOperationIsNull(firstOp)) {
1815 // Just insert at end.
1816 return PyInsertionPoint(block);
1817 }
1818
1819 // Insert before first op.
1821 block.getParentOperation()->getContext(), firstOp);
1822 return PyInsertionPoint{block, std::move(firstOpRef)};
1823}
1824
1826 MlirOperation terminator = mlirBlockGetTerminator(block.get());
1827 if (mlirOperationIsNull(terminator))
1828 throw nb::value_error("Block has no terminator");
1830 block.getParentOperation()->getContext(), terminator);
1831 return PyInsertionPoint{block, std::move(terminatorOpRef)};
1832}
1833
1835 PyOperation &operation = op.getOperation();
1836 PyBlock block = operation.getBlock();
1837 MlirOperation nextOperation = mlirOperationGetNextInBlock(operation);
1838 if (mlirOperationIsNull(nextOperation))
1839 return PyInsertionPoint(block);
1841 block.getParentOperation()->getContext(), nextOperation);
1842 return PyInsertionPoint{block, std::move(nextOpRef)};
1843}
1844
1845size_t PyMlirContext::getLiveModuleCount() { return liveModules.size(); }
1847nb::object PyInsertionPoint::contextEnter(nb::object insertPoint) {
1848 return PyThreadContextEntry::pushInsertionPoint(std::move(insertPoint));
1849}
1850
1851void PyInsertionPoint::contextExit(const nb::object &excType,
1852 const nb::object &excVal,
1853 const nb::object &excTb) {
1855}
1857//------------------------------------------------------------------------------
1858// PyAttribute.
1859//------------------------------------------------------------------------------
1861bool PyAttribute::operator==(const PyAttribute &other) const {
1862 return mlirAttributeEqual(attr, other.attr);
1863}
1865nb::object PyAttribute::getCapsule() {
1866 return nb::steal<nb::object>(mlirPythonAttributeToCapsule(*this));
1867}
1868
1869PyAttribute PyAttribute::createFromCapsule(const nb::object &capsule) {
1870 MlirAttribute rawAttr = mlirPythonCapsuleToAttribute(capsule.ptr());
1871 if (mlirAttributeIsNull(rawAttr))
1872 throw nb::python_error();
1873 return PyAttribute(
1875}
1876
1877nb::typed<nb::object, PyAttribute> PyAttribute::maybeDownCast() {
1878 MlirTypeID mlirTypeID = mlirAttributeGetTypeID(this->get());
1879 assert(!mlirTypeIDIsNull(mlirTypeID) &&
1880 "mlirTypeID was expected to be non-null.");
1881 std::optional<nb::callable> typeCaster = PyGlobals::get().lookupTypeCaster(
1882 mlirTypeID, mlirAttributeGetDialect(this->get()));
1883 // nb::rv_policy::move means use std::move to move the return value
1884 // contents into a new instance that will be owned by Python.
1885 nb::object thisObj = nb::cast(this, nb::rv_policy::move);
1886 if (!typeCaster)
1887 return thisObj;
1888 return typeCaster.value()(thisObj);
1889}
1891//------------------------------------------------------------------------------
1892// PyLocation::maybeDownCast.
1893//------------------------------------------------------------------------------
1894
1895nb::typed<nb::object, PyLocation> PyLocation::maybeDownCast() {
1896 MlirAttribute locAttr = mlirLocationGetAttribute(this->get());
1897 MlirTypeID mlirTypeID = mlirAttributeGetTypeID(locAttr);
1898 assert(!mlirTypeIDIsNull(mlirTypeID) &&
1899 "mlirTypeID was expected to be non-null.");
1900 std::optional<nb::callable> typeCaster = PyGlobals::get().lookupTypeCaster(
1901 mlirTypeID, mlirAttributeGetDialect(locAttr));
1902 nb::object thisObj = nb::cast(this, nb::rv_policy::move);
1903 if (!typeCaster)
1904 return thisObj;
1905 return typeCaster.value()(thisObj);
1906}
1908//------------------------------------------------------------------------------
1909// PyNamedAttribute.
1910//------------------------------------------------------------------------------
1911
1912PyNamedAttribute::PyNamedAttribute(MlirAttribute attr, std::string ownedName)
1913 : ownedName(new std::string(std::move(ownedName))) {
1916 toMlirStringRef(*this->ownedName)),
1917 attr);
1918}
1920//------------------------------------------------------------------------------
1921// PyType.
1922//------------------------------------------------------------------------------
1924bool PyType::operator==(const PyType &other) const {
1925 return mlirTypeEqual(type, other.type);
1926}
1928nb::object PyType::getCapsule() {
1929 return nb::steal<nb::object>(mlirPythonTypeToCapsule(*this));
1930}
1931
1932PyType PyType::createFromCapsule(nb::object capsule) {
1933 MlirType rawType = mlirPythonCapsuleToType(capsule.ptr());
1934 if (mlirTypeIsNull(rawType))
1935 throw nb::python_error();
1937 rawType);
1938}
1939
1940nb::typed<nb::object, PyType> PyType::maybeDownCast() {
1941 MlirTypeID mlirTypeID = mlirTypeGetTypeID(this->get());
1942 assert(!mlirTypeIDIsNull(mlirTypeID) &&
1943 "mlirTypeID was expected to be non-null.");
1944 std::optional<nb::callable> typeCaster = PyGlobals::get().lookupTypeCaster(
1945 mlirTypeID, mlirTypeGetDialect(this->get()));
1946 // nb::rv_policy::move means use std::move to move the return value
1947 // contents into a new instance that will be owned by Python.
1948 nb::object thisObj = nb::cast(this, nb::rv_policy::move);
1949 if (!typeCaster)
1950 return thisObj;
1951 return typeCaster.value()(thisObj);
1952}
1954//------------------------------------------------------------------------------
1955// PyTypeID.
1956//------------------------------------------------------------------------------
1958nb::object PyTypeID::getCapsule() {
1959 return nb::steal<nb::object>(mlirPythonTypeIDToCapsule(*this));
1960}
1961
1962PyTypeID PyTypeID::createFromCapsule(nb::object capsule) {
1963 MlirTypeID mlirTypeID = mlirPythonCapsuleToTypeID(capsule.ptr());
1964 if (mlirTypeIDIsNull(mlirTypeID))
1965 throw nb::python_error();
1966 return PyTypeID(mlirTypeID);
1967}
1968bool PyTypeID::operator==(const PyTypeID &other) const {
1969 return mlirTypeIDEqual(typeID, other.typeID);
1970}
1972//------------------------------------------------------------------------------
1973// PyValue and subclasses.
1974//------------------------------------------------------------------------------
1976nb::object PyValue::getCapsule() {
1977 return nb::steal<nb::object>(mlirPythonValueToCapsule(get()));
1978}
1979
1980static PyOperationRef getValueOwnerRef(MlirValue value) {
1981 MlirOperation owner;
1982 if (mlirValueIsAOpResult(value))
1983 owner = mlirOpResultGetOwner(value);
1984 else if (mlirValueIsABlockArgument(value))
1986 else
1987 assert(false && "Value must be an block arg or op result.");
1988 if (mlirOperationIsNull(owner))
1989 throw nb::python_error();
1990 MlirContext ctx = mlirOperationGetContext(owner);
1992}
1993
1994nb::typed<nb::object, std::variant<PyBlockArgument, PyOpResult, PyValue>>
1996 MlirType type = mlirValueGetType(get());
1997 MlirTypeID mlirTypeID = mlirTypeGetTypeID(type);
1998 assert(!mlirTypeIDIsNull(mlirTypeID) &&
1999 "mlirTypeID was expected to be non-null.");
2000 std::optional<nb::callable> valueCaster =
2002 // nb::rv_policy::move means use std::move to move the return value
2003 // contents into a new instance that will be owned by Python.
2004 nb::object thisObj;
2005 if (mlirValueIsAOpResult(value))
2006 thisObj = nb::cast<PyOpResult>(*this, nb::rv_policy::move);
2007 else if (mlirValueIsABlockArgument(value))
2008 thisObj = nb::cast<PyBlockArgument>(*this, nb::rv_policy::move);
2009 else
2010 assert(false && "Value must be an block arg or op result.");
2011 if (valueCaster)
2012 return valueCaster.value()(thisObj);
2013 return thisObj;
2014}
2015
2016PyValue PyValue::createFromCapsule(nb::object capsule) {
2017 MlirValue value = mlirPythonCapsuleToValue(capsule.ptr());
2018 if (mlirValueIsNull(value))
2019 throw nb::python_error();
2020 PyOperationRef ownerRef = getValueOwnerRef(value);
2021 return PyValue(ownerRef, value);
2022}
2024//------------------------------------------------------------------------------
2025// PySymbolTable.
2026//------------------------------------------------------------------------------
2027
2029 : operation(operation.getOperation().getRef()) {
2030 symbolTable = mlirSymbolTableCreate(operation.getOperation().get());
2031 if (mlirSymbolTableIsNull(symbolTable)) {
2032 throw nb::type_error("Operation is not a Symbol Table.");
2033 }
2034}
2035
2036nb::object PySymbolTable::dunderGetItem(const std::string &name) {
2037 operation->checkValid();
2038 MlirOperation symbol = mlirSymbolTableLookup(
2039 symbolTable, mlirStringRefCreate(name.data(), name.length()));
2040 if (mlirOperationIsNull(symbol))
2041 throw nb::key_error(
2042 join("Symbol '", name, "' not in the symbol table.").c_str());
2043
2044 return PyOperation::forOperation(operation->getContext(), symbol,
2045 operation.getObject())
2046 ->createOpView();
2047}
2048
2050 operation->checkValid();
2051 symbol.getOperation().checkValid();
2052 mlirSymbolTableErase(symbolTable, symbol.getOperation().get());
2053 // The operation is also erased, so we must invalidate it. There may be Python
2054 // references to this operation so we don't want to delete it from the list of
2055 // live operations here.
2056 symbol.getOperation().valid = false;
2057}
2058
2059void PySymbolTable::dunderDel(const std::string &name) {
2060 nb::object operation = dunderGetItem(name);
2061 erase(nb::cast<PyOperationBase &>(operation));
2062}
2063
2065 operation->checkValid();
2066 symbol.getOperation().checkValid();
2067 MlirAttribute symbolAttr = mlirOperationGetAttributeByName(
2069 if (mlirAttributeIsNull(symbolAttr))
2070 throw nb::value_error("Expected operation to have a symbol name.");
2072 symbol.getOperation().getContext(),
2073 mlirSymbolTableInsert(symbolTable, symbol.getOperation().get()));
2074}
2075
2077 // Op must already be a symbol.
2078 PyOperation &operation = symbol.getOperation();
2079 operation.checkValid();
2081 MlirAttribute existingNameAttr =
2082 mlirOperationGetAttributeByName(operation.get(), attrName);
2083 if (mlirAttributeIsNull(existingNameAttr))
2084 throw nb::value_error("Expected operation to have a symbol name.");
2085 return PyStringAttribute(symbol.getOperation().getContext(),
2086 existingNameAttr);
2087}
2088
2090 const std::string &name) {
2091 // Op must already be a symbol.
2092 PyOperation &operation = symbol.getOperation();
2093 operation.checkValid();
2095 MlirAttribute existingNameAttr =
2096 mlirOperationGetAttributeByName(operation.get(), attrName);
2097 if (mlirAttributeIsNull(existingNameAttr))
2098 throw nb::value_error("Expected operation to have a symbol name.");
2099 MlirAttribute newNameAttr =
2100 mlirStringAttrGet(operation.getContext()->get(), toMlirStringRef(name));
2101 mlirOperationSetAttributeByName(operation.get(), attrName, newNameAttr);
2102}
2103
2105 PyOperation &operation = symbol.getOperation();
2106 operation.checkValid();
2108 MlirAttribute existingVisAttr =
2109 mlirOperationGetAttributeByName(operation.get(), attrName);
2110 if (mlirAttributeIsNull(existingVisAttr))
2111 throw nb::value_error("Expected operation to have a symbol visibility.");
2112 return PyStringAttribute(symbol.getOperation().getContext(), existingVisAttr);
2113}
2114
2116 const std::string &visibility) {
2117 if (visibility != "public" && visibility != "private" &&
2118 visibility != "nested")
2119 throw nb::value_error(
2120 "Expected visibility to be 'public', 'private' or 'nested'");
2121 PyOperation &operation = symbol.getOperation();
2122 operation.checkValid();
2124 MlirAttribute existingVisAttr =
2125 mlirOperationGetAttributeByName(operation.get(), attrName);
2126 if (mlirAttributeIsNull(existingVisAttr))
2127 throw nb::value_error("Expected operation to have a symbol visibility.");
2128 MlirAttribute newVisAttr = mlirStringAttrGet(operation.getContext()->get(),
2129 toMlirStringRef(visibility));
2130 mlirOperationSetAttributeByName(operation.get(), attrName, newVisAttr);
2131}
2132
2133void PySymbolTable::replaceAllSymbolUses(const std::string &oldSymbol,
2134 const std::string &newSymbol,
2135 PyOperationBase &from) {
2136 PyOperation &fromOperation = from.getOperation();
2137 fromOperation.checkValid();
2139 toMlirStringRef(oldSymbol), toMlirStringRef(newSymbol),
2141
2142 throw nb::value_error("Symbol rename failed");
2143}
2144
2146 bool allSymUsesVisible,
2147 nb::object callback) {
2148 PyOperation &fromOperation = from.getOperation();
2149 fromOperation.checkValid();
2150 struct UserData {
2151 PyMlirContextRef context;
2152 nb::object callback;
2153 bool gotException;
2154 std::string exceptionWhat;
2155 nb::object exceptionType;
2156 };
2157 UserData userData{
2158 fromOperation.getContext(), std::move(callback), false, {}, {}};
2160 fromOperation.get(), allSymUsesVisible,
2161 [](MlirOperation foundOp, bool isVisible, void *calleeUserDataVoid) {
2162 UserData *calleeUserData = static_cast<UserData *>(calleeUserDataVoid);
2163 auto pyFoundOp =
2164 PyOperation::forOperation(calleeUserData->context, foundOp);
2165 if (calleeUserData->gotException)
2166 return;
2167 try {
2168 calleeUserData->callback(pyFoundOp.getObject(), isVisible);
2169 } catch (nb::python_error &e) {
2170 calleeUserData->gotException = true;
2171 calleeUserData->exceptionWhat = e.what();
2172 calleeUserData->exceptionType = nb::borrow(e.type());
2173 }
2174 },
2175 static_cast<void *>(&userData));
2176 if (userData.gotException) {
2177 std::string message("Exception raised in callback: ");
2178 message.append(userData.exceptionWhat);
2179 throw std::runtime_error(message);
2180 }
2181}
2182
2183void PyBlockArgument::bindDerived(ClassTy &c) {
2184 c.def_prop_ro(
2185 "owner",
2186 [](PyBlockArgument &self) {
2187 return PyBlock(self.getParentOperation(),
2189 },
2190 "Returns the block that owns this argument.");
2191 c.def_prop_ro(
2192 "arg_number",
2193 [](PyBlockArgument &self) {
2194 return mlirBlockArgumentGetArgNumber(self.get());
2195 },
2196 "Returns the position of this argument in the block's argument list.");
2197 c.def(
2198 "set_type",
2199 [](PyBlockArgument &self, PyType type) {
2200 return mlirBlockArgumentSetType(self.get(), type);
2201 },
2202 "type"_a, "Sets the type of this block argument.");
2203 c.def(
2204 "set_location",
2205 [](PyBlockArgument &self, PyLocation loc) {
2207 },
2208 "loc"_a, "Sets the location of this block argument.");
2209}
2210
2212 MlirBlock block, intptr_t startIndex,
2215 length == -1 ? mlirBlockGetNumArguments(block) : length, step),
2216 operation(std::move(operation)), block(block) {}
2217
2218void PyBlockArgumentList::bindDerived(ClassTy &c) {
2219 c.def_prop_ro(
2220 "types",
2221 [](PyBlockArgumentList &self) {
2222 return getValueTypes(self, self.operation->getContext());
2223 },
2224 "Returns a list of types for all arguments in this argument list.");
2225}
2226
2227intptr_t PyBlockArgumentList::getRawNumElements() {
2228 operation->checkValid();
2229 return mlirBlockGetNumArguments(block);
2230}
2231
2232PyBlockArgument PyBlockArgumentList::getRawElement(intptr_t pos) const {
2233 MlirValue argument = mlirBlockGetArgument(block, pos);
2234 return PyBlockArgument(operation, argument);
2235}
2236
2237PyBlockArgumentList PyBlockArgumentList::slice(intptr_t startIndex,
2239 intptr_t step) const {
2240 return PyBlockArgumentList(operation, block, startIndex, length, step);
2241}
2242
2244 intptr_t length, intptr_t step)
2245 : Sliceable(startIndex,
2247 : length,
2248 step),
2249 operation(operation) {}
2250
2253 mlirOperationSetOperand(operation->get(), index, value.get());
2254}
2255
2256void PyOpOperandList::bindDerived(ClassTy &c) {
2257 c.def("__setitem__", &PyOpOperandList::dunderSetItem, "index"_a, "value"_a,
2258 "Sets the operand at the specified index to a new value.");
2259}
2260
2261intptr_t PyOpOperandList::getRawNumElements() {
2262 operation->checkValid();
2263 return mlirOperationGetNumOperands(operation->get());
2264}
2265
2266PyValue PyOpOperandList::getRawElement(intptr_t pos) {
2267 MlirValue operand = mlirOperationGetOperand(operation->get(), pos);
2268 PyOperationRef pyOwner = getValueOwnerRef(operand);
2269 return PyValue(pyOwner, operand);
2270}
2271
2272PyOpOperandList PyOpOperandList::slice(intptr_t startIndex, intptr_t length,
2273 intptr_t step) const {
2274 return PyOpOperandList(operation, startIndex, length, step);
2275}
2277/// A list of OpOperands. Internally, these are stored as consecutive elements,
2278/// random access is cheap. The (returned) OpOperand list is associated with the
2279/// operation whose operands these are, and thus extends the lifetime of this
2280/// operation.
2281class PyOpOperands : public Sliceable<PyOpOperands, PyOpOperand> {
2282public:
2283 static constexpr const char *pyClassName = "OpOperands";
2285
2287 intptr_t length = -1, intptr_t step = 1)
2289 length == -1 ? mlirOperationGetNumOperands(operation->get())
2290 : length,
2291 step),
2292 operation(operation) {}
2293
2294private:
2295 /// Give the parent CRTP class access to hook implementations below.
2296 friend class Sliceable<PyOpOperands, PyOpOperand>;
2297
2298 intptr_t getRawNumElements() {
2299 operation->checkValid();
2300 return mlirOperationGetNumOperands(operation->get());
2301 }
2302
2303 PyOpOperand getRawElement(intptr_t pos) {
2304 MlirOpOperand opOperand = mlirOperationGetOpOperand(operation->get(), pos);
2305 return PyOpOperand(opOperand);
2306 }
2307
2309 return PyOpOperands(operation, startIndex, length, step);
2311
2312 PyOperationRef operation;
2313};
2314
2316 intptr_t length, intptr_t step)
2317 : Sliceable(startIndex,
2319 : length,
2320 step),
2321 operation(operation) {}
2322
2325 mlirOperationSetSuccessor(operation->get(), index, block.get());
2326}
2327
2328void PyOpSuccessors::bindDerived(ClassTy &c) {
2329 c.def("__setitem__", &PyOpSuccessors::dunderSetItem, "index"_a, "block"_a,
2330 "Sets the successor block at the specified index.");
2331}
2332
2333intptr_t PyOpSuccessors::getRawNumElements() {
2334 operation->checkValid();
2335 return mlirOperationGetNumSuccessors(operation->get());
2336}
2337
2338PyBlock PyOpSuccessors::getRawElement(intptr_t pos) {
2339 MlirBlock block = mlirOperationGetSuccessor(operation->get(), pos);
2340 return PyBlock(operation, block);
2341}
2342
2344 intptr_t step) const {
2345 return PyOpSuccessors(operation, startIndex, length, step);
2346}
2347
2349 intptr_t startIndex, intptr_t length,
2350 intptr_t step)
2351 : Sliceable(startIndex,
2352 length == -1 ? mlirBlockGetNumSuccessors(block.get()) : length,
2353 step),
2354 operation(operation), block(block) {}
2355
2356intptr_t PyBlockSuccessors::getRawNumElements() {
2357 block.checkValid();
2358 return mlirBlockGetNumSuccessors(block.get());
2359}
2360
2361PyBlock PyBlockSuccessors::getRawElement(intptr_t pos) {
2362 MlirBlock block = mlirBlockGetSuccessor(this->block.get(), pos);
2363 return PyBlock(operation, block);
2364}
2365
2367 intptr_t step) const {
2368 return PyBlockSuccessors(block, operation, startIndex, length, step);
2369}
2370
2372 PyOperationRef operation,
2373 intptr_t startIndex, intptr_t length,
2374 intptr_t step)
2375 : Sliceable(startIndex,
2376 length == -1 ? mlirBlockGetNumPredecessors(block.get())
2377 : length,
2378 step),
2379 operation(operation), block(block) {}
2380
2381intptr_t PyBlockPredecessors::getRawNumElements() {
2382 block.checkValid();
2383 return mlirBlockGetNumPredecessors(block.get());
2384}
2385
2386PyBlock PyBlockPredecessors::getRawElement(intptr_t pos) {
2387 MlirBlock block = mlirBlockGetPredecessor(this->block.get(), pos);
2388 return PyBlock(operation, block);
2389}
2390
2391PyBlockPredecessors PyBlockPredecessors::slice(intptr_t startIndex,
2392 intptr_t length,
2393 intptr_t step) const {
2394 return PyBlockPredecessors(block, operation, startIndex, length, step);
2395}
2396
2397nb::typed<nb::object, PyAttribute>
2398PyOpAttributeMap::dunderGetItemNamed(const std::string &name) {
2399 MlirAttribute attr =
2401 if (mlirAttributeIsNull(attr)) {
2402 throw nb::key_error("attempt to access a non-existent attribute");
2404 return PyAttribute(operation->getContext(), attr).maybeDownCast();
2405}
2406
2407nb::typed<nb::object, std::optional<PyAttribute>>
2408PyOpAttributeMap::get(const std::string &key, nb::object defaultValue) {
2409 MlirAttribute attr =
2411 if (mlirAttributeIsNull(attr))
2412 return defaultValue;
2413 return PyAttribute(operation->getContext(), attr).maybeDownCast();
2414}
2415
2417 if (index < 0) {
2418 index += dunderLen();
2419 }
2420 if (index < 0 || index >= dunderLen()) {
2421 throw nb::index_error("attempt to access out of bounds attribute");
2422 }
2423 MlirNamedAttribute namedAttr =
2424 mlirOperationGetAttribute(operation->get(), index);
2425 return PyNamedAttribute(
2426 namedAttr.attribute,
2427 std::string(mlirIdentifierStr(namedAttr.name).data,
2428 mlirIdentifierStr(namedAttr.name).length));
2429}
2430
2431void PyOpAttributeMap::dunderSetItem(const std::string &name,
2432 const PyAttribute &attr) {
2433 mlirOperationSetAttributeByName(operation->get(), toMlirStringRef(name),
2434 attr);
2435}
2436
2437void PyOpAttributeMap::dunderDelItem(const std::string &name) {
2438 int removed = mlirOperationRemoveAttributeByName(operation->get(),
2440 if (!removed)
2441 throw nb::key_error("attempt to delete a non-existent attribute");
2442}
2445 return mlirOperationGetNumAttributes(operation->get());
2446}
2447
2448bool PyOpAttributeMap::dunderContains(const std::string &name) {
2449 return !mlirAttributeIsNull(
2450 mlirOperationGetAttributeByName(operation->get(), toMlirStringRef(name)));
2451}
2452
2454 MlirOperation op, std::function<void(MlirStringRef, MlirAttribute)> fn) {
2456 for (intptr_t i = 0; i < n; ++i) {
2459 fn(name, na.attribute);
2460 }
2461}
2462
2463void PyOpAttributeMap::bind(nb::module_ &m) {
2464 nb::class_<PyOpAttributeMap>(m, "OpAttributeMap")
2465 .def("__contains__", &PyOpAttributeMap::dunderContains, "name"_a,
2466 "Checks if an attribute with the given name exists in the map.")
2467 .def("__len__", &PyOpAttributeMap::dunderLen,
2468 "Returns the number of attributes in the map.")
2469 .def("__getitem__", &PyOpAttributeMap::dunderGetItemNamed, "name"_a,
2470 "Gets an attribute by name.")
2471 .def("__getitem__", &PyOpAttributeMap::dunderGetItemIndexed, "index"_a,
2472 "Gets a named attribute by index.")
2473 .def("__setitem__", &PyOpAttributeMap::dunderSetItem, "name"_a, "attr"_a,
2474 "Sets an attribute with the given name.")
2475 .def("__delitem__", &PyOpAttributeMap::dunderDelItem, "name"_a,
2476 "Deletes an attribute with the given name.")
2477 .def("get", &PyOpAttributeMap::get, nb::arg("key"),
2478 nb::arg("default") = nb::none(),
2479 "Gets an attribute by name or the default value, if it does not "
2480 "exist.")
2481 .def(
2482 "__iter__",
2483 [](PyOpAttributeMap &self) -> nb::typed<nb::iterator, nb::str> {
2484 nb::list keys;
2486 self.operation->get(), [&](MlirStringRef name, MlirAttribute) {
2487 keys.append(nb::str(name.data, name.length));
2488 });
2489 return nb::iter(keys);
2490 },
2491 "Iterates over attribute names.")
2492 .def(
2493 "keys",
2494 [](PyOpAttributeMap &self) -> nb::typed<nb::list, nb::str> {
2495 nb::list out;
2497 self.operation->get(), [&](MlirStringRef name, MlirAttribute) {
2498 out.append(nb::str(name.data, name.length));
2499 });
2500 return out;
2501 },
2502 "Returns a list of attribute names.")
2503 .def(
2504 "values",
2505 [](PyOpAttributeMap &self) -> nb::typed<nb::list, PyAttribute> {
2506 nb::list out;
2508 self.operation->get(), [&](MlirStringRef, MlirAttribute attr) {
2509 out.append(PyAttribute(self.operation->getContext(), attr)
2510 .maybeDownCast());
2511 });
2512 return out;
2513 },
2514 "Returns a list of attribute values.")
2515 .def(
2516 "items",
2517 [](PyOpAttributeMap &self)
2518 -> nb::typed<nb::list,
2519 nb::typed<nb::tuple, nb::str, PyAttribute>> {
2520 nb::list out;
2522 self.operation->get(),
2523 [&](MlirStringRef name, MlirAttribute attr) {
2524 out.append(nb::make_tuple(
2525 nb::str(name.data, name.length),
2526 PyAttribute(self.operation->getContext(), attr)
2527 .maybeDownCast()));
2528 });
2529 return out;
2530 },
2531 "Returns a list of `(name, attribute)` tuples.");
2532}
2533
2534void PyOpAdaptor::bind(nb::module_ &m) {
2535 nb::class_<PyOpAdaptor>(m, "OpAdaptor")
2536 .def(nb::init<nb::typed<nb::list, PyValue>, PyOpAttributeMap>(),
2537 "Creates an OpAdaptor with the given operands and attributes.",
2538 "operands"_a, "attributes"_a)
2539 .def(nb::init<nb::typed<nb::list, PyValue>, PyOpView &>(),
2540 "Creates an OpAdaptor with the given operands and operation view.",
2541 "operands"_a, "opview"_a)
2542 .def_prop_ro(
2543 "operands", [](PyOpAdaptor &self) { return self.operands; },
2544 "Returns the operands of the adaptor.")
2545 .def_prop_ro(
2546 "attributes", [](PyOpAdaptor &self) { return self.attributes; },
2547 "Returns the attributes of the adaptor.");
2548}
2549
2550static MlirLogicalResult verifyTraitByMethod(MlirOperation op, void *userData,
2551 const char *methodName) {
2552 nb::handle targetObj(static_cast<PyObject *>(userData));
2553 if (!nb::hasattr(targetObj, methodName))
2554 return mlirLogicalResultSuccess();
2556 nb::object opView = PyOperation::forOperation(ctx, op)->createOpView();
2557 bool success = nb::cast<bool>(targetObj.attr(methodName)(opView));
2559};
2560
2561static bool attachOpTrait(const nb::object &opName, MlirDynamicOpTrait trait,
2562 PyMlirContext &context) {
2563 std::string opNameStr;
2564 if (opName.is_type()) {
2565 opNameStr = nb::cast<std::string>(opName.attr("OPERATION_NAME"));
2566 } else if (nb::isinstance<nb::str>(opName)) {
2567 opNameStr = nb::cast<std::string>(opName);
2568 } else {
2569 throw nb::type_error("the root argument must be a type or a string");
2570 }
2573 trait, MlirStringRef{opNameStr.data(), opNameStr.size()}, context.get());
2574}
2575
2576bool PyDynamicOpTrait::attach(const nb::object &opName,
2577 const nb::object &target,
2578 PyMlirContext &context) {
2579 if (!nb::hasattr(target, "verify_invariants") &&
2580 !nb::hasattr(target, "verify_region_invariants"))
2581 throw nb::type_error(
2582 "the target object must have at least one of 'verify_invariants' or "
2583 "'verify_region_invariants' methods");
2584
2586 callbacks.construct = [](void *userData) {
2587 nb::handle(static_cast<PyObject *>(userData)).inc_ref();
2588 };
2589 callbacks.destruct = [](void *userData) {
2590 nb::handle(static_cast<PyObject *>(userData)).dec_ref();
2591 };
2592
2593 callbacks.verifyTrait = [](MlirOperation op,
2594 void *userData) -> MlirLogicalResult {
2595 return verifyTraitByMethod(op, userData, "verify_invariants");
2596 };
2597 callbacks.verifyRegionTrait = [](MlirOperation op,
2598 void *userData) -> MlirLogicalResult {
2599 return verifyTraitByMethod(op, userData, "verify_region_invariants");
2600 };
2601
2602 // To ensure that the same dynamic trait gets the same TypeID despite how many
2603 // times `attach` is called, we store it as an attribute on the target class.
2604 if (!nb::hasattr(target, typeIDAttr)) {
2605 nb::setattr(target, typeIDAttr,
2606 nb::cast(PyTypeID(PyGlobals::get().allocateTypeID())));
2607 }
2608 MlirDynamicOpTrait trait = mlirDynamicOpTraitCreate(
2609 nb::cast<PyTypeID>(target.attr(typeIDAttr)).get(), callbacks,
2610 static_cast<void *>(target.ptr()));
2611 return attachOpTrait(opName, trait, context);
2612}
2613
2614void PyDynamicOpTrait::bind(nb::module_ &m) {
2615 nb::class_<PyDynamicOpTrait> cls(m, "DynamicOpTrait");
2616 cls.attr("attach") = classmethod(
2617 [](const nb::object &cls,
2618 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2619 &opName,
2620 nb::object target, DefaultingPyMlirContext context) {
2621 if (target.is_none())
2622 target = cls;
2623 return PyDynamicOpTrait::attach(opName, target, *context.get());
2624 },
2625 nb::arg("cls"), nb::arg("op_name"), nb::arg("target").none() = nb::none(),
2626 nb::arg("context").none() = nb::none(),
2627 "Attach the dynamic op trait subclass to the given operation name.");
2628}
2629
2630bool PyDynamicOpTraits::IsTerminator::attach(const nb::object &opName,
2631 PyMlirContext &context) {
2632 MlirDynamicOpTrait trait = mlirDynamicOpTraitIsTerminatorCreate();
2633 return attachOpTrait(opName, trait, context);
2634}
2635
2636void PyDynamicOpTraits::IsTerminator::bind(nb::module_ &m) {
2637 nb::class_<PyDynamicOpTraits::IsTerminator, PyDynamicOpTrait> cls(
2638 m, "IsTerminatorTrait");
2639 cls.def_prop_ro_static(typeIDAttr, [](nanobind::object & /*class*/) {
2641 });
2642 cls.attr("attach") = classmethod(
2643 [](const nb::object &cls,
2644 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2645 &opName,
2646 DefaultingPyMlirContext context) {
2647 return PyDynamicOpTraits::IsTerminator::attach(opName, *context.get());
2649 "Attach IsTerminator trait to the given operation name.", nb::arg("cls"),
2650 nb::arg("op_name"), nb::arg("context").none() = nb::none());
2651}
2652
2653bool PyDynamicOpTraits::NoTerminator::attach(const nb::object &opName,
2654 PyMlirContext &context) {
2655 MlirDynamicOpTrait trait = mlirDynamicOpTraitNoTerminatorCreate();
2656 return attachOpTrait(opName, trait, context);
2657}
2658
2659void PyDynamicOpTraits::NoTerminator::bind(nb::module_ &m) {
2660 nb::class_<PyDynamicOpTraits::NoTerminator, PyDynamicOpTrait> cls(
2661 m, "NoTerminatorTrait");
2662 cls.def_prop_ro_static(typeIDAttr, [](nanobind::object & /*class*/) {
2664 });
2665 cls.attr("attach") = classmethod(
2666 [](const nb::object &cls,
2667 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2668 &opName,
2669 DefaultingPyMlirContext context) {
2670 return PyDynamicOpTraits::NoTerminator::attach(opName, *context.get());
2672 "Attach NoTerminator trait to the given operation name.", nb::arg("cls"),
2673 nb::arg("op_name"), nb::arg("context").none() = nb::none());
2674}
2675
2676bool PyDynamicOpTraits::IsIsolatedFromAbove::attach(const nb::object &opName,
2677 PyMlirContext &context) {
2678 MlirDynamicOpTrait trait = mlirDynamicOpTraitIsIsolatedFromAboveCreate();
2679 return attachOpTrait(opName, trait, context);
2680}
2681
2683 nb::class_<PyDynamicOpTraits::IsIsolatedFromAbove, PyDynamicOpTrait> cls(
2684 m, "IsIsolatedFromAboveTrait");
2685 cls.def_prop_ro_static(typeIDAttr, [](nanobind::object & /*class*/) {
2687 });
2688 cls.attr("attach") = classmethod(
2689 [](const nb::object &cls,
2690 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2691 &opName,
2692 DefaultingPyMlirContext context) {
2694 *context.get());
2695 },
2696 "Attach IsIsolatedFromAbove trait to the given operation name.",
2697 nb::arg("cls"), nb::arg("op_name"),
2698 nb::arg("context").none() = nb::none());
2699}
2700
2701bool PyDynamicOpTraits::RecursiveMemoryEffects::attach(const nb::object &opName,
2702 PyMlirContext &context) {
2703 MlirDynamicOpTrait trait = mlirDynamicOpTraitRecursiveMemoryEffectsCreate();
2704 return attachOpTrait(opName, trait, context);
2705}
2706
2708 nb::class_<PyDynamicOpTraits::RecursiveMemoryEffects, PyDynamicOpTrait> cls(
2709 m, "RecursiveMemoryEffectsTrait");
2710 cls.def_prop_ro_static(typeIDAttr, [](nanobind::object & /*class*/) {
2712 });
2713 cls.attr("attach") = classmethod(
2714 [](const nb::object &cls,
2715 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2716 &opName,
2717 DefaultingPyMlirContext context) {
2719 opName, *context.get());
2720 },
2721 "Attach RecursiveMemoryEffects trait to the given operation name.",
2722 nb::arg("cls"), nb::arg("op_name"),
2723 nb::arg("context").none() = nb::none());
2724}
2725
2726} // namespace MLIR_BINDINGS_PYTHON_DOMAIN
2727} // namespace python
2728} // namespace mlir
2729
2730namespace {
2731
2732using namespace mlir::python::MLIR_BINDINGS_PYTHON_DOMAIN;
2733
2734MlirLocation tracebackToLocation(MlirContext ctx) {
2735#if defined(Py_LIMITED_API)
2736 // Frame introspection C APIs are not available under the limited API.
2737 // Traceback-based auto-location is not supported; return unknown.
2738 return mlirLocationUnknownGet(ctx);
2739#else
2740 size_t framesLimit =
2742 // Use a thread_local here to avoid requiring a large amount of space.
2743 thread_local std::array<MlirLocation, PyGlobals::TracebackLoc::kMaxFrames>
2744 frames;
2745 size_t count = 0;
2746
2747 nb::gil_scoped_acquire acquire;
2748
2749 PyThreadState *tstate = PyThreadState_GET();
2750 PyFrameObject *next;
2751 PyFrameObject *pyFrame = PyThreadState_GetFrame(tstate);
2752 // In the increment expression:
2753 // 1. get the next prev frame;
2754 // 2. decrement the ref count on the current frame (in order that it can get
2755 // gc'd, along with any objects in its closure and etc);
2756 // 3. set current = next.
2757 for (; pyFrame != nullptr && count < framesLimit;
2758 next = PyFrame_GetBack(pyFrame), Py_XDECREF(pyFrame), pyFrame = next) {
2759 PyCodeObject *code = PyFrame_GetCode(pyFrame);
2760 auto fileNameStr =
2761 nb::cast<std::string>(nb::borrow<nb::str>(code->co_filename));
2762 std::string_view fileName(fileNameStr);
2763 if (!PyGlobals::get().getTracebackLoc().isUserTracebackFilename(fileName))
2764 continue;
2765
2766 // co_qualname and PyCode_Addr2Location added in py3.11
2767#if PY_VERSION_HEX < 0x030B00F0
2768 std::string name =
2769 nb::cast<std::string>(nb::borrow<nb::str>(code->co_name));
2770 std::string_view funcName(name);
2771 int startLine = PyFrame_GetLineNumber(pyFrame);
2772 MlirLocation loc = mlirLocationFileLineColGet(
2773 ctx, mlirStringRefCreate(fileName.data(), fileName.size()), startLine,
2774 0);
2775#else
2776 std::string name =
2777 nb::cast<std::string>(nb::borrow<nb::str>(code->co_qualname));
2778 std::string_view funcName(name);
2779 int startLine, startCol, endLine, endCol;
2780 int lasti = PyFrame_GetLasti(pyFrame);
2781 if (!PyCode_Addr2Location(code, lasti, &startLine, &startCol, &endLine,
2782 &endCol)) {
2783 throw nb::python_error();
2784 }
2785 MlirLocation loc = mlirLocationFileLineColRangeGet(
2786 ctx, mlirStringRefCreate(fileName.data(), fileName.size()), startLine,
2787 startCol, endLine, endCol);
2788#endif
2789
2790 frames[count] = mlirLocationNameGet(
2791 ctx, mlirStringRefCreate(funcName.data(), funcName.size()), loc);
2792 ++count;
2793 }
2794 // When the loop breaks (after the last iter), current frame (if non-null)
2795 // is leaked without this.
2796 Py_XDECREF(pyFrame);
2797
2798 if (count == 0)
2799 return mlirLocationUnknownGet(ctx);
2800
2801 MlirLocation callee = frames[0];
2802 assert(!mlirLocationIsNull(callee) && "expected non-null callee location");
2803 if (count == 1)
2804 return callee;
2805
2806 MlirLocation caller = frames[count - 1];
2807 assert(!mlirLocationIsNull(caller) && "expected non-null caller location");
2808 for (int i = count - 2; i >= 1; i--)
2809 caller = mlirLocationCallSiteGet(frames[i], caller);
2810
2811 return mlirLocationCallSiteGet(callee, caller);
2812#endif
2813}
2814
2815/// Apply currentLocAction: wrap or fuse Location.current onto baseLoc.
2816static MlirLocation
2817applyCurrentLocAction(MlirContext ctx, MlirLocation baseLoc,
2820 if (action == Action::Fallback)
2821 return baseLoc;
2822
2823 auto *currentLoc = PyThreadContextEntry::getDefaultLocation();
2824 if (!currentLoc)
2825 return baseLoc;
2826 assert(mlirLocationGetContext(currentLoc->get()).ptr == ctx.ptr &&
2827 "Location.current must belong to the current MLIR context");
2828
2829 // NamelocWrap: walk the NameLoc chain on Location.current, collect scope
2830 // names, wrap baseLoc innermost-first so result is Outer(Inner(baseLoc)).
2831 // If Location.current is not a NameLoc, scopeNames is empty and baseLoc
2832 // is returned unchanged (nameloc_wrap is a no-op for non-NameLoc contexts).
2833 thread_local std::vector<MlirStringRef> scopeNames;
2834 scopeNames.clear();
2835 MlirLocation walk = currentLoc->get();
2836 while (mlirLocationIsAName(walk)) {
2837 scopeNames.push_back(mlirIdentifierStr(mlirLocationNameGetName(walk)));
2839 }
2840 for (auto it = scopeNames.rbegin(); it != scopeNames.rend(); ++it)
2841 baseLoc = mlirLocationNameGet(ctx, *it, baseLoc);
2842 return baseLoc;
2843}
2844
2845PyLocation
2846maybeGetTracebackLocation(const std::optional<PyLocation> &location) {
2847 auto &tbl = PyGlobals::get().getTracebackLoc();
2848
2849 // Tracebacks not enabled — return explicit loc or fall back to
2850 // Location.current.
2851 if (!tbl.locTracebacksEnabled())
2852 return location.has_value() ? location.value()
2854
2855 // From here: tracebacks are enabled.
2857 PyMlirContext &ctx = DefaultingPyMlirContext::resolve();
2858 MlirLocation baseLoc;
2859
2860 // Step 1: on_explicit — resolve explicit loc= vs traceback.
2861 if (location.has_value()) {
2862 switch (tbl.tracebackActionOnExplicitLoc()) {
2863 case OnExplicit::UseExplicit:
2864 baseLoc = location->get();
2865 break;
2866 case OnExplicit::UseTraceback:
2867 baseLoc = tracebackToLocation(ctx.get());
2868 break;
2869 }
2870 } else {
2871 baseLoc = tracebackToLocation(ctx.get());
2872 }
2873
2874 // Step 2: current_loc — compose with Location.current.
2875 baseLoc = applyCurrentLocAction(ctx.get(), baseLoc,
2876 tbl.tracebackActionOnCurrentLoc());
2877
2879 return {ref, baseLoc};
2880}
2881} // namespace
2882
2883namespace mlir {
2884namespace python {
2886
2887static std::string formatMLIRError(const MLIRError &e) {
2888 auto locStr = [](const PyLocation &loc) {
2889 PyPrintAccumulator accum;
2890 mlirLocationPrint(loc, accum.getCallback(), accum.getUserData());
2891 std::string s = nb::cast<std::string>(nb::str(accum.join()));
2892 std::string_view sv(s);
2893 if (sv.size() > 5) {
2894 sv.remove_prefix(4); // "loc("
2895 sv.remove_suffix(1); // ")"
2896 }
2897 return std::string(sv);
2898 };
2899 auto indent = [](std::string s) {
2900 size_t pos = 0;
2901 while ((pos = s.find('\n', pos)) != std::string::npos) {
2902 s.replace(pos, 1, "\n ");
2903 pos += 3;
2904 }
2905 return s;
2906 };
2907
2908 std::ostringstream os;
2909 os << e.message;
2910 if (!e.errorDiagnostics.empty())
2911 os << ":";
2912 for (const auto &diag : e.errorDiagnostics) {
2913 os << "\nerror: " << locStr(diag.location) << ": " << indent(diag.message);
2914 for (const auto &note : diag.notes) {
2915 os << "\n note: " << locStr(note.location) << ": "
2916 << indent(note.message);
2917 }
2918 }
2919 return os.str();
2920}
2921
2922void MLIRError::bind(nb::module_ &m) {
2923 auto cls = nb::exception<MLIRError>(m, "MLIRError", PyExc_Exception);
2924 nb::register_exception_translator(
2925 [](const std::exception_ptr &p, void *payload) {
2926 try {
2927 if (p)
2928 std::rethrow_exception(p);
2929 } catch (MLIRError &e) {
2930 std::string formatted = formatMLIRError(e);
2931 nb::object ty = nb::borrow(static_cast<PyObject *>(payload));
2932 nb::object obj = ty(formatted);
2933 obj.attr("_message") = nb::cast(std::move(e.message));
2934 obj.attr("_error_diagnostics") =
2935 nb::cast(std::move(e.errorDiagnostics));
2936 PyErr_SetObject(static_cast<PyObject *>(payload), obj.ptr());
2937 }
2938 },
2939 cls.ptr());
2940 auto propertyType = nb::borrow<nb::type_object>(
2941 reinterpret_cast<PyObject *>(&PyProperty_Type));
2942 nb::setattr(
2943 cls, "message",
2944 propertyType(nb::cpp_function(
2945 [](nb::object self) -> nb::str { return self.attr("_message"); },
2946 nb::is_method())));
2947 nb::setattr(cls, "error_diagnostics",
2948 propertyType(nb::cpp_function(
2949 [](nb::object self)
2950 -> nb::typed<nb::list, PyDiagnostic::DiagnosticInfo> {
2951 return self.attr("_error_diagnostics");
2952 },
2953 nb::is_method())));
2954}
2955
2956void populateRoot(nb::module_ &m) {
2957 m.attr("T") = nb::type_var("T");
2958 m.attr("U") = nb::type_var("U");
2959
2960 // Policies for how loc_tracebacks() composes the three location sources
2961 // (explicit loc=, generated traceback, Location.current).
2962 nb::enum_<PyGlobals::TracebackLoc::OnExplicitAction>(m, "OnExplicitAction")
2963 .value("USE_EXPLICIT",
2965 .value("USE_TRACEBACK",
2967
2968 nb::enum_<PyGlobals::TracebackLoc::CurrentLocAction>(m, "CurrentLocAction")
2970 .value("NAMELOC_WRAP",
2972
2973 nb::class_<PyGlobals>(m, "_Globals")
2974 .def_prop_rw("dialect_search_modules",
2977 .def("append_dialect_search_prefix", &PyGlobals::addDialectSearchPrefix,
2978 "module_name"_a)
2979 .def(
2980 "_check_dialect_module_loaded",
2981 [](PyGlobals &self, const std::string &dialectNamespace) {
2982 return self.loadDialectModule(dialectNamespace);
2983 },
2984 "dialect_namespace"_a)
2985 .def("_register_dialect_impl", &PyGlobals::registerDialectImpl,
2986 "dialect_namespace"_a, "dialect_class"_a, nb::kw_only(),
2987 "replace"_a = false,
2988 "Testing hook for directly registering a dialect")
2989 .def("_register_operation_impl", &PyGlobals::registerOperationImpl,
2990 "operation_name"_a, "operation_class"_a, nb::kw_only(),
2991 "replace"_a = false,
2992 "Testing hook for directly registering an operation")
2993 .def("loc_tracebacks_enabled",
2994 [](PyGlobals &self) {
2995 return self.getTracebackLoc().locTracebacksEnabled();
2996 })
2997 .def("set_loc_tracebacks_enabled",
2998 [](PyGlobals &self, bool enabled) {
3000 })
3001 .def("loc_tracebacks_frame_limit",
3002 [](PyGlobals &self) {
3004 })
3005 .def("set_loc_tracebacks_frame_limit",
3006 [](PyGlobals &self, std::optional<int> n) {
3009 })
3010 .def("register_traceback_file_inclusion",
3011 [](PyGlobals &self, const std::string &filename) {
3013 })
3014 .def("register_traceback_file_exclusion",
3015 [](PyGlobals &self, const std::string &filename) {
3017 })
3018 .def("traceback_action_on_explicit_loc",
3019 [](PyGlobals &self) {
3021 })
3022 .def("set_traceback_action_on_explicit_loc",
3023 [](PyGlobals &self,
3026 })
3027 .def("traceback_action_on_current_loc",
3028 [](PyGlobals &self) {
3030 })
3031 .def("set_traceback_action_on_current_loc",
3032 [](PyGlobals &self,
3035 });
3036
3037 // Aside from making the globals accessible to python, having python manage
3038 // it is necessary to make sure it is destroyed (and releases its python
3039 // resources) properly.
3040 m.attr("globals") = nb::cast(new PyGlobals, nb::rv_policy::take_ownership);
3041
3042 // Registration decorators.
3043 m.def(
3044 "register_dialect",
3045 [](nb::type_object pyClass) {
3046 std::string dialectNamespace =
3047 nb::cast<std::string>(pyClass.attr("DIALECT_NAMESPACE"));
3048 PyGlobals::get().registerDialectImpl(dialectNamespace, pyClass);
3049 return pyClass;
3050 },
3051 "dialect_class"_a,
3052 "Class decorator for registering a custom Dialect wrapper");
3053 m.def(
3054 "register_operation",
3055 [](const nb::type_object &dialectClass, bool replace) -> nb::object {
3056 return nb::cpp_function(
3057 [dialectClass,
3058 replace](nb::type_object opClass) -> nb::type_object {
3059 std::string operationName =
3060 nb::cast<std::string>(opClass.attr("OPERATION_NAME"));
3061 PyGlobals::get().registerOperationImpl(operationName, opClass,
3062 replace);
3063 // Dict-stuff the new opClass by name onto the dialect class.
3064 nb::object opClassName = opClass.attr("__name__");
3065 dialectClass.attr(opClassName) = opClass;
3066 return opClass;
3067 });
3068 },
3069 // clang-format off
3070 nb::sig("def register_operation(dialect_class: type, *, replace: bool = False) "
3071 "-> typing.Callable[[type[T]], type[T]]"),
3072 // clang-format on
3073 "dialect_class"_a, nb::kw_only(), "replace"_a = false,
3074 "Produce a class decorator for registering an Operation class as part of "
3075 "a dialect");
3076 m.def(
3077 "register_op_adaptor",
3078 [](const nb::type_object &opClass, bool replace) -> nb::object {
3079 return nb::cpp_function(
3080 [opClass,
3081 replace](nb::type_object adaptorClass) -> nb::type_object {
3082 std::string operationName =
3083 nb::cast<std::string>(adaptorClass.attr("OPERATION_NAME"));
3084 PyGlobals::get().registerOpAdaptorImpl(operationName,
3085 adaptorClass, replace);
3086 // Dict-stuff the new adaptorClass by name onto the opClass.
3087 opClass.attr("Adaptor") = adaptorClass;
3088 return adaptorClass;
3089 });
3090 },
3091 // clang-format off
3092 nb::sig("def register_op_adaptor(op_class: type, *, replace: bool = False) "
3093 "-> typing.Callable[[type[T]], type[T]]"),
3094 // clang-format on
3095 "op_class"_a, nb::kw_only(), "replace"_a = false,
3096 "Produce a class decorator for registering an OpAdaptor class for an "
3097 "operation.");
3098 m.def(
3100 [](PyTypeID mlirTypeID, bool replace) -> nb::object {
3101 return nb::cpp_function([mlirTypeID, replace](
3102 nb::callable typeCaster) -> nb::object {
3103 PyGlobals::get().registerTypeCaster(mlirTypeID, typeCaster, replace);
3104 return typeCaster;
3105 });
3106 },
3107 // clang-format off
3108 nb::sig("def register_type_caster(typeid: _mlir.ir.TypeID, *, replace: bool = False) "
3109 "-> typing.Callable[[typing.Callable[[T], U]], typing.Callable[[T], U]]"),
3110 // clang-format on
3111 "typeid"_a, nb::kw_only(), "replace"_a = false,
3112 "Register a type caster for casting MLIR types to custom user types.");
3113 m.def(
3115 [](PyTypeID mlirTypeID, bool replace) -> nb::object {
3116 return nb::cpp_function(
3117 [mlirTypeID, replace](nb::callable valueCaster) -> nb::object {
3118 PyGlobals::get().registerValueCaster(mlirTypeID, valueCaster,
3119 replace);
3120 return valueCaster;
3121 });
3122 },
3123 // clang-format off
3124 nb::sig("def register_value_caster(typeid: _mlir.ir.TypeID, *, replace: bool = False) "
3125 "-> typing.Callable[[typing.Callable[[T], U]], typing.Callable[[T], U]]"),
3126 // clang-format on
3127 "typeid"_a, nb::kw_only(), "replace"_a = false,
3128 "Register a value caster for casting MLIR values to custom user values.");
3129}
3130
3131//------------------------------------------------------------------------------
3132// Location subclass bindDerived implementations.
3133//------------------------------------------------------------------------------
3134
3136 c.def_static(
3137 "get",
3138 [](DefaultingPyMlirContext context) {
3139 return PyUnknownLocation(context->getRef(),
3140 mlirLocationUnknownGet(context->get()));
3141 },
3142 "context"_a = nb::none(),
3143 "Gets a Location representing an unknown location.");
3144}
3145
3147 c.def_static(
3148 "get",
3149 [](std::string filename, int line, int col,
3150 DefaultingPyMlirContext context) {
3151 return PyFileLineColLocation(
3152 context->getRef(),
3154 toMlirStringRef(filename), line, col));
3155 },
3156 "filename"_a, "line"_a, "col"_a, "context"_a = nb::none(),
3157 "Gets a FileLineColLoc for a file, line, and column.");
3158 c.def_static(
3159 "get",
3160 [](std::string filename, int startLine, int startCol, int endLine,
3161 int endCol, DefaultingPyMlirContext context) {
3162 return PyFileLineColLocation(
3163 context->getRef(), mlirLocationFileLineColRangeGet(
3164 context->get(), toMlirStringRef(filename),
3165 startLine, startCol, endLine, endCol));
3166 },
3167 "filename"_a, "start_line"_a, "start_col"_a, "end_line"_a, "end_col"_a,
3168 "context"_a = nb::none(),
3169 "Gets a FileLineColLoc spanning a file and line/column range.");
3170 c.def_prop_ro(
3171 "filename",
3172 [](PyFileLineColLocation &self) {
3173 return mlirIdentifierStr(
3175 },
3176 "Gets the filename from a `FileLineColLoc`.");
3177 c.def_prop_ro(
3178 "start_line",
3179 [](PyFileLineColLocation &self) {
3181 },
3182 "Gets the start line number from a `FileLineColLoc`.");
3183 c.def_prop_ro(
3184 "start_col",
3185 [](PyFileLineColLocation &self) {
3187 },
3188 "Gets the start column number from a `FileLineColLoc`.");
3189 c.def_prop_ro(
3190 "end_line",
3191 [](PyFileLineColLocation &self) {
3193 },
3194 "Gets the end line number from a `FileLineColLoc`.");
3195 c.def_prop_ro(
3196 "end_col",
3197 [](PyFileLineColLocation &self) {
3199 },
3200 "Gets the end column number from a `FileLineColLoc`.");
3201}
3202
3204 c.def_static(
3205 "get",
3206 [](std::string name, std::optional<PyLocation> childLoc,
3207 DefaultingPyMlirContext context) {
3208 return PyNameLocation(
3209 context->getRef(),
3210 mlirLocationNameGet(context->get(), toMlirStringRef(name),
3211 childLoc
3212 ? childLoc->get()
3213 : mlirLocationUnknownGet(context->get())));
3214 },
3215 "name"_a, "child_loc"_a = nb::none(), "context"_a = nb::none(),
3216 "Gets a NameLoc with an optional child location.");
3217 c.def_prop_ro(
3218 "name_str",
3219 [](PyNameLocation &self) {
3221 },
3222 "Gets the name string from a `NameLoc`.");
3223 c.def_prop_ro(
3224 "child_loc",
3225 [](PyNameLocation &self) {
3226 return PyLocation(self.getContext(),
3228 .maybeDownCast();
3229 },
3230 "Gets the child location from a `NameLoc`.");
3231}
3232
3234 c.def_static(
3235 "get",
3236 [](PyLocation callee, const std::vector<PyLocation> &frames,
3237 DefaultingPyMlirContext context) {
3238 if (frames.empty())
3239 throw nb::value_error("No caller frames provided.");
3240 MlirLocation caller = frames.back().get();
3241 for (size_t index = frames.size() - 1; index-- > 0;) {
3242 caller = mlirLocationCallSiteGet(frames[index].get(), caller);
3243 }
3244 return PyCallSiteLocation(
3245 context->getRef(), mlirLocationCallSiteGet(callee.get(), caller));
3246 },
3247 "callee"_a, "frames"_a, "context"_a = nb::none(),
3248 "Gets a CallSiteLoc chaining a callee and one or more caller frames.");
3249 c.def_prop_ro(
3250 "callee",
3251 [](PyCallSiteLocation &self) {
3252 return PyLocation(self.getContext(),
3254 .maybeDownCast();
3255 },
3256 "Gets the callee location from a `CallSiteLoc`.");
3257 c.def_prop_ro(
3258 "caller",
3259 [](PyCallSiteLocation &self) {
3260 return PyLocation(self.getContext(),
3262 .maybeDownCast();
3263 },
3264 "Gets the caller location from a `CallSiteLoc`.");
3265}
3266
3268 c.def_static(
3269 "get",
3270 [](const std::vector<PyLocation> &pyLocations,
3271 std::optional<PyAttribute> metadata, DefaultingPyMlirContext context) {
3272 std::vector<MlirLocation> locations;
3273 locations.reserve(pyLocations.size());
3274 for (const PyLocation &pyLocation : pyLocations)
3275 locations.push_back(pyLocation.get());
3276 MlirLocation location = mlirLocationFusedGet(
3277 context->get(), locations.size(), locations.data(),
3278 metadata ? metadata->get() : MlirAttribute{0});
3279 // Strict: `Location.fused(...)` handles the collapse case.
3280 if (!mlirLocationIsAFused(location))
3281 throw nb::value_error(
3282 "FusedLoc.get would collapse to a non-fused location; use "
3283 "Location.fused(...) for the permissive variant.");
3284 return PyFusedLocation(context->getRef(), location);
3285 },
3286 "locations"_a, "metadata"_a = nb::none(), "context"_a = nb::none(),
3287 "Gets a FusedLoc from an array of locations and optional metadata. "
3288 "Raises if the fuse would collapse to a non-fused location; use "
3289 "`Location.fused(...)` for the permissive variant.");
3290 c.def_prop_ro(
3291 "locations",
3292 [](PyFusedLocation &self) {
3293 unsigned numLocations = mlirLocationFusedGetNumLocations(self.get());
3294 std::vector<MlirLocation> locations(numLocations);
3295 if (numLocations)
3296 mlirLocationFusedGetLocations(self.get(), locations.data());
3297 std::vector<nb::object> pyLocations;
3298 pyLocations.reserve(numLocations);
3299 for (unsigned i = 0; i < numLocations; ++i)
3300 pyLocations.push_back(
3301 PyLocation(self.getContext(), locations[i]).maybeDownCast());
3302 return pyLocations;
3303 },
3304 "Gets the list of locations from a `FusedLoc`.");
3305 c.def_prop_ro(
3306 "metadata",
3307 [](PyFusedLocation &self) -> std::optional<PyAttribute> {
3308 MlirAttribute metadata = mlirLocationFusedGetMetadata(self.get());
3309 if (mlirAttributeIsNull(metadata))
3310 return std::nullopt;
3311 return PyAttribute(self.getContext(), metadata);
3312 },
3313 "Gets the metadata attribute from a `FusedLoc`, or None if absent.");
3314}
3315
3316//------------------------------------------------------------------------------
3317// Populates the core exports of the 'ir' submodule.
3318//------------------------------------------------------------------------------
3319void populateIRCore(nb::module_ &m) {
3320 //----------------------------------------------------------------------------
3321 // Enums.
3322 //----------------------------------------------------------------------------
3323 nb::enum_<PyDiagnosticSeverity>(m, "DiagnosticSeverity")
3324 .value("ERROR", PyDiagnosticSeverity::Error)
3325 .value("WARNING", PyDiagnosticSeverity::Warning)
3326 .value("NOTE", PyDiagnosticSeverity::Note)
3327 .value("REMARK", PyDiagnosticSeverity::Remark);
3328
3329 nb::enum_<PyWalkOrder>(m, "WalkOrder")
3330 .value("PRE_ORDER", PyWalkOrder::PreOrder)
3331 .value("POST_ORDER", PyWalkOrder::PostOrder);
3332
3333 nb::enum_<PyOperationEquivalenceFlags>(m, "OperationEquivalenceFlags",
3334 nb::is_arithmetic(), nb::is_flag())
3335 .value("NONE", PyOperationEquivalenceFlags::None)
3336 .value("IGNORE_LOCATIONS", PyOperationEquivalenceFlags::IgnoreLocations)
3337 .value("IGNORE_DISCARDABLE_ATTRS",
3339 .value("IGNORE_PROPERTIES", PyOperationEquivalenceFlags::IgnoreProperties)
3340 .value("IGNORE_COMMUTATIVITY",
3342
3343 nb::enum_<PyWalkResult>(m, "WalkResult")
3344 .value("ADVANCE", PyWalkResult::Advance)
3345 .value("INTERRUPT", PyWalkResult::Interrupt)
3346 .value("SKIP", PyWalkResult::Skip);
3347
3348 //----------------------------------------------------------------------------
3349 // Mapping of Diagnostics.
3350 //----------------------------------------------------------------------------
3351 nb::class_<PyDiagnostic>(m, "Diagnostic")
3352 .def_prop_ro("severity", &PyDiagnostic::getSeverity,
3353 "Returns the severity of the diagnostic.")
3354 .def_prop_ro("location", &PyDiagnostic::getLocation,
3355 "Returns the location associated with the diagnostic.")
3356 .def_prop_ro("message", &PyDiagnostic::getMessage,
3357 "Returns the message text of the diagnostic.")
3358 .def_prop_ro("notes", &PyDiagnostic::getNotes,
3359 "Returns a tuple of attached note diagnostics.")
3360 .def(
3361 "__str__",
3362 [](PyDiagnostic &self) -> nb::str {
3363 if (!self.isValid())
3364 return nb::str("<Invalid Diagnostic>");
3365 return self.getMessage();
3366 },
3367 "Returns the diagnostic message as a string.");
3368
3369 nb::class_<PyDiagnostic::DiagnosticInfo>(m, "DiagnosticInfo")
3370 .def(
3371 "__init__",
3373 new (&self) PyDiagnostic::DiagnosticInfo(diag.getInfo());
3374 },
3375 "diag"_a, "Creates a DiagnosticInfo from a Diagnostic.")
3376 .def_ro("severity", &PyDiagnostic::DiagnosticInfo::severity,
3377 "The severity level of the diagnostic.")
3378 .def_ro("location", &PyDiagnostic::DiagnosticInfo::location,
3379 "The location associated with the diagnostic.")
3380 .def_ro("message", &PyDiagnostic::DiagnosticInfo::message,
3381 "The message text of the diagnostic.")
3382 .def_ro("notes", &PyDiagnostic::DiagnosticInfo::notes,
3383 "List of attached note diagnostics.")
3384 .def(
3385 "__str__",
3386 [](PyDiagnostic::DiagnosticInfo &self) { return self.message; },
3387 "Returns the diagnostic message as a string.");
3388
3389 nb::class_<PyDiagnosticHandler>(m, "DiagnosticHandler")
3390 .def("detach", &PyDiagnosticHandler::detach,
3391 "Detaches the diagnostic handler from the context.")
3392 .def_prop_ro("attached", &PyDiagnosticHandler::isAttached,
3393 "Returns True if the handler is attached to a context.")
3394 .def_prop_ro("had_error", &PyDiagnosticHandler::getHadError,
3395 "Returns True if an error was encountered during diagnostic "
3396 "handling.")
3397 .def("__enter__", &PyDiagnosticHandler::contextEnter,
3398 "Enters the diagnostic handler as a context manager.",
3399 nb::sig("def __enter__(self, /) -> DiagnosticHandler"))
3400 .def("__exit__", &PyDiagnosticHandler::contextExit, "exc_type"_a.none(),
3401 "exc_value"_a.none(), "traceback"_a.none(),
3402 "Exits the diagnostic handler context manager.");
3403
3404 // Expose DefaultThreadPool to python
3405 nb::class_<PyThreadPool>(m, "ThreadPool")
3406 .def(
3407 "__init__", [](PyThreadPool &self) { new (&self) PyThreadPool(); },
3408 "Creates a new thread pool with default concurrency.")
3409 .def("get_max_concurrency", &PyThreadPool::getMaxConcurrency,
3410 "Returns the maximum number of threads in the pool.")
3411 .def("_mlir_thread_pool_ptr", &PyThreadPool::_mlir_thread_pool_ptr,
3412 "Returns the raw pointer to the LLVM thread pool as a string.");
3413
3414 nb::class_<PyMlirContext>(m, "Context")
3415 .def(
3416 "__init__",
3417 [](PyMlirContext &self) {
3418 MlirContext context = mlirContextCreateWithThreading(false);
3419 new (&self) PyMlirContext(context);
3420 },
3421 R"(
3422 Creates a new MLIR context.
3423
3424 The context is the top-level container for all MLIR objects. It owns the storage
3425 for types, attributes, locations, and other core IR objects. A context can be
3426 configured to allow or disallow unregistered dialects and can have dialects
3427 loaded on-demand.)")
3428 .def_static("_get_live_count", &PyMlirContext::getLiveCount,
3429 "Gets the number of live Context objects.")
3430 .def(
3431 "_get_context_again",
3432 [](PyMlirContext &self) -> nb::typed<nb::object, PyMlirContext> {
3434 return ref.releaseObject();
3435 },
3436 "Gets another reference to the same context.")
3437 .def("_get_live_module_count", &PyMlirContext::getLiveModuleCount,
3438 "Gets the number of live modules owned by this context.")
3440 "Gets a capsule wrapping the MlirContext.")
3443 "Creates a Context from a capsule wrapping MlirContext.")
3444 .def("__enter__", &PyMlirContext::contextEnter,
3445 "Enters the context as a context manager.",
3446 nb::sig("def __enter__(self, /) -> Context"))
3447 .def("__exit__", &PyMlirContext::contextExit, "exc_type"_a.none(),
3448 "exc_value"_a.none(), "traceback"_a.none(),
3449 "Exits the context manager.")
3450 .def_prop_ro_static(
3451 "current",
3452 [](nb::object & /*class*/)
3453 -> std::optional<nb::typed<nb::object, PyMlirContext>> {
3455 if (!context)
3456 return {};
3457 return nb::cast(context);
3458 },
3459 nb::sig("def current(/) -> Context | None"),
3460 "Gets the Context bound to the current thread or returns None if no "
3461 "context is set.")
3462 .def_prop_ro(
3463 "dialects",
3464 [](PyMlirContext &self) { return PyDialects(self.getRef()); },
3465 "Gets a container for accessing dialects by name.")
3466 .def_prop_ro(
3467 "d", [](PyMlirContext &self) { return PyDialects(self.getRef()); },
3468 "Alias for `dialects`.")
3469 .def(
3470 "get_dialect_descriptor",
3471 [](PyMlirContext &self, std::string &name) {
3472 MlirDialect dialect = mlirContextGetOrLoadDialect(
3473 self.get(), {name.data(), name.size()});
3474 if (mlirDialectIsNull(dialect)) {
3475 throw nb::value_error(
3476 join("Dialect '", name, "' not found").c_str());
3477 }
3478 return PyDialectDescriptor(self.getRef(), dialect);
3479 },
3480 "dialect_name"_a,
3481 "Gets or loads a dialect by name, returning its descriptor object.")
3482 .def(
3483 "is_dialect_loaded",
3484 [](PyMlirContext &self, std::string &name) {
3485 MlirDialect dialect = mlirContextGetLoadedDialect(
3486 self.get(), {name.data(), name.size()});
3487 return !mlirDialectIsNull(dialect);
3488 },
3489 "dialect_name"_a, "Checks if a dialect is loaded in the context.")
3490 .def_prop_rw(
3491 "allow_unregistered_dialects",
3492 [](PyMlirContext &self) -> bool {
3493 return mlirContextGetAllowUnregisteredDialects(self.get());
3494 },
3495 [](PyMlirContext &self, bool value) {
3496 mlirContextSetAllowUnregisteredDialects(self.get(), value);
3497 },
3498 "Controls whether unregistered dialects are allowed in this context.")
3499 .def("attach_diagnostic_handler", &PyMlirContext::attachDiagnosticHandler,
3500 "callback"_a,
3501 "Attaches a diagnostic handler that will receive callbacks.")
3502 .def(
3503 "enable_multithreading",
3504 [](PyMlirContext &self, bool enable) {
3505 mlirContextEnableMultithreading(self.get(), enable);
3506 },
3507 "enable"_a,
3508 R"(
3509 Enables or disables multi-threading support in the context.
3510
3511 Args:
3512 enable: Whether to enable (True) or disable (False) multi-threading.
3513 )")
3514 .def(
3515 "set_thread_pool",
3516 [](PyMlirContext &self, PyThreadPool &pool) {
3517 // we should disable multi-threading first before setting
3518 // new thread pool otherwise the assert in
3519 // MLIRContext::setThreadPool will be raised.
3520 mlirContextEnableMultithreading(self.get(), false);
3521 mlirContextSetThreadPool(self.get(), pool.get());
3522 },
3523 R"(
3524 Sets a custom thread pool for the context to use.
3525
3526 Args:
3527 pool: A ThreadPool object to use for parallel operations.
3528
3529 Note:
3530 Multi-threading is automatically disabled before setting the thread pool.)")
3531 .def(
3532 "get_num_threads",
3533 [](PyMlirContext &self) {
3534 return mlirContextGetNumThreads(self.get());
3535 },
3536 "Gets the number of threads in the context's thread pool.")
3537 .def(
3538 "_mlir_thread_pool_ptr",
3539 [](PyMlirContext &self) {
3540 MlirLlvmThreadPool pool = mlirContextGetThreadPool(self.get());
3541 std::stringstream ss;
3542 ss << pool.ptr;
3543 return ss.str();
3544 },
3545 "Gets the raw pointer to the LLVM thread pool as a string.")
3546 .def(
3547 "is_registered_operation",
3548 [](PyMlirContext &self, std::string &name) {
3550 self.get(), MlirStringRef{name.data(), name.size()});
3551 },
3552 "operation_name"_a,
3553 R"(
3554 Checks whether an operation with the given name is registered.
3555
3556 Args:
3557 operation_name: The fully qualified name of the operation (e.g., `arith.addf`).
3558
3559 Returns:
3560 True if the operation is registered, False otherwise.)")
3561 .def(
3562 "append_dialect_registry",
3563 [](PyMlirContext &self, PyDialectRegistry &registry) {
3564 mlirContextAppendDialectRegistry(self.get(), registry);
3565 },
3566 "registry"_a,
3567 R"(
3568 Appends the contents of a dialect registry to the context.
3569
3570 Args:
3571 registry: A DialectRegistry containing dialects to append.)")
3572 .def_prop_rw("emit_error_diagnostics",
3575 R"(
3576 Controls whether error diagnostics are emitted to diagnostic handlers.
3577
3578 By default, error diagnostics are captured and reported through MLIRError exceptions.)")
3579 .def(
3580 "load_all_available_dialects",
3581 [](PyMlirContext &self) {
3583 },
3584 R"(
3585 Loads all dialects available in the registry into the context.
3586
3587 This eagerly loads all dialects that have been registered, making them
3588 immediately available for use.)")
3589 .def(
3590 "begin_transient_scope",
3591 [](PyMlirContext &self) {
3592 if (mlirContextIsInTransientScope(self.get()))
3593 throw nb::value_error("Context is already in a transient scope");
3595 },
3596 R"(
3597 Begins a transient scope on the context, freezing the base layer.
3598
3599 All subsequently allocated types, attributes, and unregistered operations
3600 are treated as transient and will be deallocated with end_transient_scope().
3601 Raises a ValueError if the context is already in a transient scope.)")
3602 .def(
3603 "end_transient_scope",
3604 [](PyMlirContext &self) { mlirContextEndTransientScope(self.get()); },
3605 R"(
3606 Ends the transient scope and resets the context to the base state.
3607
3608 Prunes all transient types, attributes, affine expressions, distinct
3609 attributes, and unregistered operations added during the transient scope.
3610
3611 Note: Any Python objects referencing transient IR entities become invalid
3612 after this call and must not be accessed.)")
3613 .def_prop_ro(
3614 "is_in_transient_scope",
3615 [](PyMlirContext &self) -> bool {
3616 return mlirContextIsInTransientScope(self.get());
3617 },
3618 "Returns whether the context is currently in a transient scope.");
3619
3620 //----------------------------------------------------------------------------
3621 // Mapping of PyDialectDescriptor
3622 //----------------------------------------------------------------------------
3623 nb::class_<PyDialectDescriptor>(m, "DialectDescriptor")
3624 .def_prop_ro(
3625 "namespace",
3626 [](PyDialectDescriptor &self) {
3627 MlirStringRef ns = mlirDialectGetNamespace(self.get());
3628 return nb::str(ns.data, ns.length);
3629 },
3630 "Returns the namespace of the dialect.")
3631 .def(
3632 "__repr__",
3633 [](PyDialectDescriptor &self) {
3634 MlirStringRef ns = mlirDialectGetNamespace(self.get());
3635 std::string repr("<DialectDescriptor ");
3636 repr.append(ns.data, ns.length);
3637 repr.append(">");
3638 return repr;
3639 },
3640 nb::sig("def __repr__(self) -> str"),
3641 "Returns a string representation of the dialect descriptor.");
3642
3643 //----------------------------------------------------------------------------
3644 // Mapping of PyDialects
3645 //----------------------------------------------------------------------------
3646 nb::class_<PyDialects>(m, "Dialects")
3647 .def(
3648 "__getitem__",
3649 [](PyDialects &self, std::string keyName) {
3650 MlirDialect dialect =
3651 self.getDialectForKey(keyName, /*attrError=*/false);
3652 nb::object descriptor =
3653 nb::cast(PyDialectDescriptor{self.getContext(), dialect});
3654 return createCustomDialectWrapper(keyName, std::move(descriptor));
3655 },
3656 "Gets a dialect by name using subscript notation.")
3657 .def(
3658 "__getattr__",
3659 [](PyDialects &self, std::string attrName) {
3660 MlirDialect dialect =
3661 self.getDialectForKey(attrName, /*attrError=*/true);
3662 nb::object descriptor =
3663 nb::cast(PyDialectDescriptor{self.getContext(), dialect});
3664 return createCustomDialectWrapper(attrName, std::move(descriptor));
3665 },
3666 "Gets a dialect by name using attribute notation.");
3667
3668 //----------------------------------------------------------------------------
3669 // Mapping of PyDialect
3670 //----------------------------------------------------------------------------
3671 nb::class_<PyDialect>(m, "Dialect")
3672 .def(nb::init<nb::object>(), "descriptor"_a,
3673 "Creates a Dialect from a DialectDescriptor.")
3674 .def_prop_ro(
3675 "descriptor", [](PyDialect &self) { return self.getDescriptor(); },
3676 "Returns the DialectDescriptor for this dialect.")
3677 .def(
3678 "__repr__",
3679 [](const nb::object &self) {
3680 auto clazz = self.attr("__class__");
3681 return nb::str("<Dialect ") +
3682 self.attr("descriptor").attr("namespace") +
3683 nb::str(" (class ") + clazz.attr("__module__") +
3684 nb::str(".") + clazz.attr("__name__") + nb::str(")>");
3685 },
3686 nb::sig("def __repr__(self) -> str"),
3687 "Returns a string representation of the dialect.");
3688
3689 //----------------------------------------------------------------------------
3690 // Mapping of PyDialectRegistry
3691 //----------------------------------------------------------------------------
3692 nb::class_<PyDialectRegistry>(m, "DialectRegistry")
3694 "Gets a capsule wrapping the MlirDialectRegistry.")
3697 "Creates a DialectRegistry from a capsule wrapping "
3698 "`MlirDialectRegistry`.")
3699 .def(nb::init<>(), "Creates a new empty dialect registry.");
3700
3701 //----------------------------------------------------------------------------
3702 // Mapping of Location
3703 //----------------------------------------------------------------------------
3704 nb::class_<PyLocation>(m, "Location")
3706 "Gets a capsule wrapping the MlirLocation.")
3708 "Creates a Location from a capsule wrapping MlirLocation.")
3709 .def("__enter__", &PyLocation::contextEnter,
3710 "Enters the location as a context manager.",
3711 nb::sig("def __enter__(self, /) -> Location"))
3712 .def("__exit__", &PyLocation::contextExit, "exc_type"_a.none(),
3713 "exc_value"_a.none(), "traceback"_a.none(),
3714 "Exits the location context manager.")
3715 .def(
3716 "__eq__",
3717 [](PyLocation &self, PyLocation &other) -> bool {
3718 return mlirLocationEqual(self, other);
3719 },
3720 "Compares two locations for equality.")
3721 .def(
3722 "__eq__", [](PyLocation &self, nb::object other) { return false; },
3723 "Compares location with non-location object (always returns False).")
3724 .def_prop_ro_static(
3725 "current",
3726 [](nb::object & /*class*/) -> std::optional<PyLocation *> {
3728 if (!loc)
3729 return std::nullopt;
3730 return loc;
3731 },
3732 // clang-format off
3733 nb::sig("def current(/) -> Location | None"),
3734 // clang-format on
3735 "Gets the Location bound to the current thread or raises ValueError.")
3736 .def_static(
3737 "from_attr",
3738 [](PyAttribute &attribute, DefaultingPyMlirContext context) {
3739 return PyLocation(context->getRef(),
3740 mlirLocationFromAttribute(attribute))
3741 .maybeDownCast();
3742 },
3743 "attribute"_a, "context"_a = nb::none(),
3744 "Gets a Location from a `LocationAttr`.")
3745 // Factory shims kept for backward compatibility; return the concrete
3746 // subclass. New code should use the subclass `.get()` directly.
3747 .def_static(
3748 "unknown",
3749 [](DefaultingPyMlirContext context) {
3750 return PyUnknownLocation(context->getRef(),
3751 mlirLocationUnknownGet(context->get()));
3752 },
3753 "context"_a = nb::none(), "Alias for `UnknownLoc.get()`.")
3754 .def_static(
3755 "file",
3756 [](std::string filename, int line, int col,
3757 DefaultingPyMlirContext context) {
3758 return PyFileLineColLocation(
3759 context->getRef(),
3761 context->get(), toMlirStringRef(filename), line, col));
3762 },
3763 "filename"_a, "line"_a, "col"_a, "context"_a = nb::none(),
3764 "Alias for `FileLineColLoc.get()`.")
3765 .def_static(
3766 "file",
3767 [](std::string filename, int startLine, int startCol, int endLine,
3768 int endCol, DefaultingPyMlirContext context) {
3769 return PyFileLineColLocation(
3770 context->getRef(),
3772 context->get(), toMlirStringRef(filename), startLine,
3773 startCol, endLine, endCol));
3774 },
3775 "filename"_a, "start_line"_a, "start_col"_a, "end_line"_a,
3776 "end_col"_a, "context"_a = nb::none(),
3777 "Alias for `FileLineColLoc.get()` over a range.")
3778 .def_static(
3779 "name",
3780 [](std::string name, std::optional<PyLocation> childLoc,
3781 DefaultingPyMlirContext context) {
3782 return PyNameLocation(
3783 context->getRef(),
3785 context->get(), toMlirStringRef(name),
3786 childLoc ? childLoc->get()
3787 : mlirLocationUnknownGet(context->get())));
3788 },
3789 "name"_a, "childLoc"_a = nb::none(), "context"_a = nb::none(),
3790 "Alias for `NameLoc.get()`.")
3791 .def_static(
3792 "callsite",
3793 [](PyLocation callee, const std::vector<PyLocation> &frames,
3794 DefaultingPyMlirContext context) {
3795 if (frames.empty())
3796 throw nb::value_error("No caller frames provided.");
3797 MlirLocation caller = frames.back().get();
3798 for (size_t index = frames.size() - 1; index-- > 0;)
3799 caller = mlirLocationCallSiteGet(frames[index].get(), caller);
3800 return PyCallSiteLocation(
3801 context->getRef(),
3802 mlirLocationCallSiteGet(callee.get(), caller));
3803 },
3804 "callee"_a, "frames"_a, "context"_a = nb::none(),
3805 "Alias for `CallSiteLoc.get()`.")
3806 .def_static(
3807 "fused",
3808 [](const std::vector<PyLocation> &pyLocations,
3809 std::optional<PyAttribute> metadata,
3810 DefaultingPyMlirContext context) {
3811 std::vector<MlirLocation> locations;
3812 locations.reserve(pyLocations.size());
3813 for (const PyLocation &pyLocation : pyLocations)
3814 locations.push_back(pyLocation.get());
3815 MlirLocation location = mlirLocationFusedGet(
3816 context->get(), locations.size(), locations.data(),
3817 metadata ? metadata->get() : MlirAttribute{0});
3818 return PyLocation(context->getRef(), location).maybeDownCast();
3819 },
3820 "locations"_a, "metadata"_a = nb::none(), "context"_a = nb::none(),
3821 "Alias for `FusedLoc.get()` (may collapse to a non-fused location).")
3822 .def_prop_ro(
3823 "context",
3824 [](PyLocation &self) -> nb::typed<nb::object, PyMlirContext> {
3825 return self.getContext().getObject();
3826 },
3827 "Context that owns the `Location`.")
3828 .def_prop_ro(
3829 "attr",
3830 [](PyLocation &self) {
3831 return PyAttribute(self.getContext(),
3833 },
3834 "Get the underlying `LocationAttr`.")
3835 .def_prop_ro(
3836 "typeid",
3837 [](PyLocation &self) {
3838 MlirTypeID mlirTypeID =
3840 assert(!mlirTypeIDIsNull(mlirTypeID) &&
3841 "mlirTypeID was expected to be non-null.");
3842 return PyTypeID(mlirTypeID);
3843 },
3844 "Gets the `TypeID` of the underlying LocationAttr.")
3845 .def(
3846 "emit_error",
3847 [](PyLocation &self, std::string message) {
3848 mlirEmitError(self, message.c_str());
3849 },
3850 "message"_a,
3851 R"(
3852 Emits an error diagnostic at this location.
3853
3854 Args:
3855 message: The error message to emit.)")
3856 .def(
3857 "__str__",
3858 [](PyLocation &self) {
3859 PyPrintAccumulator printAccum;
3860 mlirLocationPrint(self, printAccum.getCallback(),
3861 printAccum.getUserData());
3862 return printAccum.join();
3863 },
3864 "Returns the assembly form of the Location.")
3865 .def(
3866 "__repr__",
3867 [](PyLocation &self) {
3868 PyPrintAccumulator printAccum;
3869 mlirLocationPrint(self, printAccum.getCallback(),
3870 printAccum.getUserData());
3871 return printAccum.join();
3872 },
3873 "Returns the assembly representation of the location.");
3874
3880
3881 //----------------------------------------------------------------------------
3882 // Mapping of Module
3883 //----------------------------------------------------------------------------
3884 nb::class_<PyModule>(m, "Module", nb::is_weak_referenceable())
3886 "Gets a capsule wrapping the MlirModule.")
3888 R"(
3889 Creates a Module from a `MlirModule` wrapped by a capsule (i.e. `module._CAPIPtr`).
3890
3891 This returns a new object **BUT** `_clear_mlir_module(module)` must be called to
3892 prevent double-frees (of the underlying `mlir::Module`).)")
3893 .def("_clear_mlir_module", &PyModule::clearMlirModule,
3894 R"(
3895 Clears the internal MLIR module reference.
3896
3897 This is used internally to prevent double-free when ownership is transferred
3898 via the C API capsule mechanism. Not intended for normal use.)")
3899 .def_static(
3900 "parse",
3901 [](const std::string &moduleAsm, DefaultingPyMlirContext context)
3902 -> nb::typed<nb::object, PyModule> {
3903 PyMlirContext::ErrorCapture errors(context->getRef());
3904 MlirModule module = mlirModuleCreateParse(
3905 context->get(), toMlirStringRef(moduleAsm));
3906 if (mlirModuleIsNull(module))
3907 throw MLIRError("Unable to parse module assembly", errors.take());
3908 return PyModule::forModule(module).releaseObject();
3909 },
3910 "asm"_a, "context"_a = nb::none(), kModuleParseDocstring)
3911 .def_static(
3912 "parse",
3913 [](nb::bytes moduleAsm, DefaultingPyMlirContext context)
3914 -> nb::typed<nb::object, PyModule> {
3915 PyMlirContext::ErrorCapture errors(context->getRef());
3916 MlirModule module = mlirModuleCreateParse(
3917 context->get(), toMlirStringRef(moduleAsm));
3918 if (mlirModuleIsNull(module))
3919 throw MLIRError("Unable to parse module assembly", errors.take());
3920 return PyModule::forModule(module).releaseObject();
3921 },
3922 "asm"_a, "context"_a = nb::none(), kModuleParseDocstring)
3923 .def_static(
3924 "parseFile",
3925 [](const std::string &path, DefaultingPyMlirContext context)
3926 -> nb::typed<nb::object, PyModule> {
3927 PyMlirContext::ErrorCapture errors(context->getRef());
3928 MlirModule module = mlirModuleCreateParseFromFile(
3929 context->get(), toMlirStringRef(path));
3930 if (mlirModuleIsNull(module))
3931 throw MLIRError("Unable to parse module assembly", errors.take());
3932 return PyModule::forModule(module).releaseObject();
3933 },
3934 "path"_a, "context"_a = nb::none(), kModuleParseDocstring)
3935 .def_static(
3936 "create",
3937 [](const std::optional<PyLocation> &loc)
3938 -> nb::typed<nb::object, PyModule> {
3939 PyLocation pyLoc = maybeGetTracebackLocation(loc);
3940 MlirModule module = mlirModuleCreateEmpty(pyLoc.get());
3941 return PyModule::forModule(module).releaseObject();
3942 },
3943 "loc"_a = nb::none(), "Creates an empty module.")
3944 .def_prop_ro(
3945 "context",
3946 [](PyModule &self) -> nb::typed<nb::object, PyMlirContext> {
3947 return self.getContext().getObject();
3948 },
3949 "Context that created the `Module`.")
3950 .def_prop_ro(
3951 "operation",
3952 [](PyModule &self) -> nb::typed<nb::object, PyOperation> {
3953 return PyOperation::forOperation(self.getContext(),
3954 mlirModuleGetOperation(self.get()),
3955 self.getRef().releaseObject())
3956 .releaseObject();
3957 },
3958 "Accesses the module as an operation.")
3959 .def_prop_ro(
3960 "body",
3961 [](PyModule &self) {
3963 self.getContext(), mlirModuleGetOperation(self.get()),
3964 self.getRef().releaseObject());
3965 PyBlock returnBlock(moduleOp, mlirModuleGetBody(self.get()));
3966 return returnBlock;
3967 },
3968 "Return the block for this module.")
3969 .def(
3970 "dump",
3971 [](PyModule &self) {
3973 },
3975 .def(
3976 "__str__",
3977 [](const nb::object &self) {
3978 // Defer to the operation's __str__.
3979 return self.attr("operation").attr("__str__")();
3980 },
3981 nb::sig("def __str__(self) -> str"),
3982 R"(
3983 Gets the assembly form of the operation with default options.
3984
3985 If more advanced control over the assembly formatting or I/O options is needed,
3986 use the dedicated print or get_asm method, which supports keyword arguments to
3987 customize behavior.
3988 )")
3989 .def(
3990 "__eq__",
3991 [](PyModule &self, PyModule &other) {
3992 return mlirModuleEqual(self.get(), other.get());
3993 },
3994 "other"_a, "Compares two modules for equality.")
3995 .def(
3996 "__hash__",
3997 [](PyModule &self) { return mlirModuleHashValue(self.get()); },
3998 "Returns the hash value of the module.");
3999
4000 //----------------------------------------------------------------------------
4001 // Mapping of Operation.
4002 //----------------------------------------------------------------------------
4003 nb::class_<PyOperationBase>(m, "_OperationBase")
4004 .def_prop_ro(
4006 [](PyOperationBase &self) {
4007 return self.getOperation().getCapsule();
4008 },
4009 "Gets a capsule wrapping the `MlirOperation`.")
4010 .def(
4011 "__eq__",
4012 [](PyOperationBase &self, PyOperationBase &other) {
4013 return mlirOperationEqual(self.getOperation().get(),
4014 other.getOperation().get());
4015 },
4016 "Compares two operations for equality.")
4017 .def(
4018 "__eq__",
4019 [](PyOperationBase &self, nb::object other) { return false; },
4020 "Compares operation with non-operation object (always returns "
4021 "False).")
4022 .def(
4023 "__hash__",
4024 [](PyOperationBase &self) {
4025 return mlirOperationHashValue(self.getOperation().get());
4026 },
4027 "Returns the hash value of the operation.")
4028 .def(
4029 "is_structurally_equivalent",
4030 [](PyOperationBase &self, PyOperationBase &other,
4032 self.getOperation().checkValid();
4033 other.getOperation().checkValid();
4035 self.getOperation().get(), other.getOperation().get(),
4036 static_cast<uint32_t>(flags));
4037 },
4038 "other"_a, "flags"_a = PyOperationEquivalenceFlags::None,
4039 R"("Checks whether two operations are structurally equivalent. The predicate recursively compares regions.")")
4040 .def(
4041 "structural_hash",
4043 self.getOperation().checkValid();
4045 self.getOperation().get(), static_cast<uint32_t>(flags));
4046 },
4048 R"(Computes a structural hash for the operation. The hash does not recurse into regions, unlike the predicate.")")
4049 .def_prop_ro(
4050 "attributes",
4051 [](PyOperationBase &self) {
4052 return PyOpAttributeMap(self.getOperation().getRef());
4053 },
4054 "Returns a dictionary-like map of operation attributes.")
4055 .def_prop_ro(
4056 "context",
4057 [](PyOperationBase &self) -> nb::typed<nb::object, PyMlirContext> {
4058 PyOperation &concreteOperation = self.getOperation();
4059 concreteOperation.checkValid();
4060 return concreteOperation.getContext().getObject();
4061 },
4062 "Context that owns the operation.")
4063 .def_prop_ro(
4064 "name",
4065 [](PyOperationBase &self) {
4066 auto &concreteOperation = self.getOperation();
4067 concreteOperation.checkValid();
4068 MlirOperation operation = concreteOperation.get();
4069 return mlirIdentifierStr(mlirOperationGetName(operation));
4070 },
4071 "Returns the fully qualified name of the operation.")
4072 .def_prop_ro(
4073 "operands",
4074 [](PyOperationBase &self) {
4075 return PyOpOperandList(self.getOperation().getRef());
4076 },
4077 "Returns the list of operation operands.")
4078 .def_prop_ro(
4079 "op_operands",
4080 [](PyOperationBase &self) {
4081 return PyOpOperands(self.getOperation().getRef());
4082 },
4083 "Returns the list of op operands.")
4084 .def_prop_ro(
4085 "regions",
4086 [](PyOperationBase &self) {
4087 return PyRegionList(self.getOperation().getRef());
4088 },
4089 "Returns the list of operation regions.")
4090 .def_prop_ro(
4091 "results",
4092 [](PyOperationBase &self) {
4093 return PyOpResultList(self.getOperation().getRef());
4094 },
4095 "Returns the list of Operation results.")
4096 .def_prop_ro(
4097 "result",
4098 [](PyOperationBase &self) -> nb::typed<nb::object, PyOpResult> {
4099 auto &operation = self.getOperation();
4100 return PyOpResult(operation.getRef(), getUniqueResult(operation))
4101 .maybeDownCast();
4102 },
4103 "Shortcut to get an op result if it has only one (throws an error "
4104 "otherwise).")
4105 .def_prop_rw(
4106 "location",
4107 [](PyOperationBase &self) {
4108 PyOperation &operation = self.getOperation();
4109 return PyLocation(operation.getContext(),
4110 mlirOperationGetLocation(operation.get()))
4111 .maybeDownCast();
4112 },
4113 [](PyOperationBase &self, const PyLocation &location) {
4114 PyOperation &operation = self.getOperation();
4115 mlirOperationSetLocation(operation.get(), location.get());
4116 },
4117 nb::for_getter("Returns the source location the operation was "
4118 "defined or derived from."),
4119 nb::for_setter("Sets the source location the operation was defined "
4120 "or derived from."))
4121 .def_prop_ro(
4122 "parent",
4123 [](PyOperationBase &self)
4124 -> std::optional<nb::typed<nb::object, PyOperation>> {
4125 auto parent = self.getOperation().getParentOperation();
4126 if (parent)
4127 return parent->getObject();
4128 return {};
4129 },
4130 "Returns the parent operation, or `None` if at top level.")
4131 .def(
4132 "__str__",
4133 [](PyOperationBase &self) {
4134 return self.getAsm(/*binary=*/false,
4135 /*largeElementsLimit=*/std::nullopt,
4136 /*largeResourceLimit=*/std::nullopt,
4137 /*enableDebugInfo=*/false,
4138 /*prettyDebugInfo=*/false,
4139 /*printGenericOpForm=*/false,
4140 /*useLocalScope=*/false,
4141 /*useNameLocAsPrefix=*/false,
4142 /*assumeVerified=*/false,
4143 /*skipRegions=*/false);
4144 },
4145 nb::sig("def __str__(self) -> str"),
4146 "Returns the assembly form of the operation.")
4147 .def("print",
4148 nb::overload_cast<PyAsmState &, nb::object, bool>(
4150 "state"_a, "file"_a = nb::none(), "binary"_a = false,
4151 R"(
4152 Prints the assembly form of the operation to a file like object.
4153
4154 Args:
4155 state: `AsmState` capturing the operation numbering and flags.
4156 file: Optional file like object to write to. Defaults to sys.stdout.
4157 binary: Whether to write `bytes` (True) or `str` (False). Defaults to False.)")
4158 .def("print",
4159 nb::overload_cast<std::optional<int64_t>, std::optional<int64_t>,
4160 bool, bool, bool, bool, bool, bool, nb::object,
4161 bool, bool>(&PyOperationBase::print),
4162 // Careful: Lots of arguments must match up with print method.
4163 "large_elements_limit"_a = nb::none(),
4164 "large_resource_limit"_a = nb::none(), "enable_debug_info"_a = false,
4165 "pretty_debug_info"_a = false, "print_generic_op_form"_a = false,
4166 "use_local_scope"_a = false, "use_name_loc_as_prefix"_a = false,
4167 "assume_verified"_a = false, "file"_a = nb::none(),
4168 "binary"_a = false, "skip_regions"_a = false,
4169 R"(
4170 Prints the assembly form of the operation to a file like object.
4171
4172 Args:
4173 large_elements_limit: Whether to elide elements attributes above this
4174 number of elements. Defaults to None (no limit).
4175 large_resource_limit: Whether to elide resource attributes above this
4176 number of characters. Defaults to None (no limit). If large_elements_limit
4177 is set and this is None, the behavior will be to use large_elements_limit
4178 as large_resource_limit.
4179 enable_debug_info: Whether to print debug/location information. Defaults
4180 to False.
4181 pretty_debug_info: Whether to format debug information for easier reading
4182 by a human (warning: the result is unparseable). Defaults to False.
4183 print_generic_op_form: Whether to print the generic assembly forms of all
4184 ops. Defaults to False.
4185 use_local_scope: Whether to print in a way that is more optimized for
4186 multi-threaded access but may not be consistent with how the overall
4187 module prints.
4188 use_name_loc_as_prefix: Whether to use location attributes (NameLoc) as
4189 prefixes for the SSA identifiers. Defaults to False.
4190 assume_verified: By default, if not printing generic form, the verifier
4191 will be run and if it fails, generic form will be printed with a comment
4192 about failed verification. While a reasonable default for interactive use,
4193 for systematic use, it is often better for the caller to verify explicitly
4194 and report failures in a more robust fashion. Set this to True if doing this
4195 in order to avoid running a redundant verification. If the IR is actually
4196 invalid, behavior is undefined.
4197 file: The file like object to write to. Defaults to sys.stdout.
4198 binary: Whether to write bytes (True) or str (False). Defaults to False.
4199 skip_regions: Whether to skip printing regions. Defaults to False.)")
4200 .def("write_bytecode", &PyOperationBase::writeBytecode, "file"_a,
4201 "desired_version"_a = nb::none(),
4202 R"(
4203 Write the bytecode form of the operation to a file like object.
4204
4205 Args:
4206 file: The file like object to write to.
4207 desired_version: Optional version of bytecode to emit.
4208 Returns:
4209 The bytecode writer status.)")
4210 .def("get_asm", &PyOperationBase::getAsm,
4211 // Careful: Lots of arguments must match up with get_asm method.
4212 "binary"_a = false, "large_elements_limit"_a = nb::none(),
4213 "large_resource_limit"_a = nb::none(), "enable_debug_info"_a = false,
4214 "pretty_debug_info"_a = false, "print_generic_op_form"_a = false,
4215 "use_local_scope"_a = false, "use_name_loc_as_prefix"_a = false,
4216 "assume_verified"_a = false, "skip_regions"_a = false,
4217 R"(
4218 Gets the assembly form of the operation with all options available.
4219
4220 Args:
4221 binary: Whether to return a bytes (True) or str (False) object. Defaults to
4222 False.
4223 ... others ...: See the print() method for common keyword arguments for
4224 configuring the printout.
4225 Returns:
4226 Either a bytes or str object, depending on the setting of the `binary`
4227 argument.)")
4228 .def("verify", &PyOperationBase::verify,
4229 "Verify the operation. Raises MLIRError if verification fails, and "
4230 "returns true otherwise.")
4231 .def("move_after", &PyOperationBase::moveAfter, "other"_a,
4232 "Puts self immediately after the other operation in its parent "
4233 "block.")
4234 .def("move_before", &PyOperationBase::moveBefore, "other"_a,
4235 "Puts self immediately before the other operation in its parent "
4236 "block.")
4237 .def("is_before_in_block", &PyOperationBase::isBeforeInBlock, "other"_a,
4238 R"(
4239 Checks if this operation is before another in the same block.
4240
4241 Args:
4242 other: Another operation in the same parent block.
4243
4244 Returns:
4245 True if this operation is before `other` in the operation list of the parent block.)")
4246 .def(
4247 "clone",
4248 [](PyOperationBase &self,
4249 const nb::object &ip) -> nb::typed<nb::object, PyOperation> {
4250 return self.getOperation().clone(ip);
4251 },
4252 "ip"_a = nb::none(),
4253 R"(
4254 Creates a deep copy of the operation.
4255
4256 Args:
4257 ip: Optional insertion point where the cloned operation should be inserted.
4258 If None, the current insertion point is used. If False, the operation
4259 remains detached.
4260
4261 Returns:
4262 A new Operation that is a clone of this operation.)")
4263 .def(
4264 "detach_from_parent",
4265 [](PyOperationBase &self) -> nb::typed<nb::object, PyOpView> {
4266 PyOperation &operation = self.getOperation();
4267 operation.checkValid();
4268 if (!operation.isAttached())
4269 throw nb::value_error("Detached operation has no parent.");
4270
4271 operation.detachFromParent();
4272 return operation.createOpView();
4273 },
4274 "Detaches the operation from its parent block.")
4275 .def_prop_ro(
4276 "attached",
4277 [](PyOperationBase &self) {
4278 PyOperation &operation = self.getOperation();
4279 operation.checkValid();
4280 return operation.isAttached();
4281 },
4282 "Reports if the operation is attached to its parent block.")
4283 .def(
4284 "erase", [](PyOperationBase &self) { self.getOperation().erase(); },
4285 R"(
4286 Erases the operation and frees its memory.
4287
4288 Note:
4289 After erasing, any Python references to the operation become invalid.)")
4290 .def(
4291 "walk",
4292 [](PyOperationBase &self,
4293 std::function<PyWalkResult(MlirOperation)> callback,
4294 PyWalkOrder walkOrder, std::optional<nb::object> opClass) {
4295 if (!opClass)
4296 return self.walk(callback, walkOrder);
4297 self.walk(
4298 [&](MlirOperation mlirOp) -> PyWalkResult {
4299 nb::object opview =
4301 self.getOperation().getContext(), mlirOp)
4302 ->createOpView();
4303 if (nb::isinstance(opview, *opClass))
4304 return callback(mlirOp);
4305 return PyWalkResult::Advance;
4306 },
4307 walkOrder);
4308 },
4309 "callback"_a, "walk_order"_a = PyWalkOrder::PostOrder,
4310 "op_class"_a = nb::none(),
4311 // clang-format off
4312 nb::sig("def walk(self, callback: Callable[[Operation], WalkResult], walk_order: WalkOrder = ..., op_class: type[OpView] | None = None) -> None"),
4313 // clang-format on
4314 R"(
4315 Walks the operation tree with a callback function.
4316
4317 If op_class is provided, the callback is only invoked on operations
4318 of that type; all other operations are skipped silently.
4319
4320 Args:
4321 callback: A callable that takes an Operation and returns a WalkResult.
4322 walk_order: The order of traversal (PRE_ORDER or POST_ORDER).
4323 op_class: If provided, only operations of this type are passed to the callback.)")
4324 .def(
4325 "has_trait",
4326 [](PyOperationBase &self, nb::type_object &traitCls) {
4327 PyTypeID traitTypeID =
4328 nb::cast<PyTypeID>(traitCls.attr(PyDynamicOpTrait::typeIDAttr));
4329 MlirIdentifier opName =
4330 mlirOperationGetName(self.getOperation().get());
4332 mlirIdentifierStr(opName), traitTypeID.get(),
4333 self.getOperation().getContext()->get());
4334 },
4335 "trait_cls"_a, "Checks if the operation has a given trait.");
4336
4337 nb::class_<PyOperation, PyOperationBase>(m, "Operation")
4338 .def_static(
4339 "create",
4340 [](std::string_view name,
4341 std::optional<std::vector<PyType *>> results,
4342 std::optional<std::vector<PyValue *>> operands,
4343 std::optional<nb::typed<nb::dict, nb::str, PyAttribute>>
4344 attributes,
4345 std::optional<std::vector<PyBlock *>> successors, int regions,
4346 const std::optional<PyLocation> &location,
4347 const nb::object &maybeIp,
4348 bool inferType) -> nb::typed<nb::object, PyOperation> {
4349 // Unpack/validate operands.
4350 std::vector<MlirValue> mlirOperands;
4351 if (operands) {
4352 mlirOperands.reserve(operands->size());
4353 for (PyValue *operand : *operands) {
4354 if (!operand)
4355 throw nb::value_error("operand value cannot be None");
4356 mlirOperands.push_back(operand->get());
4357 }
4358 }
4359
4360 PyLocation pyLoc = maybeGetTracebackLocation(location);
4361 return PyOperation::create(
4362 name, results, mlirOperands.data(), mlirOperands.size(),
4363 attributes, successors, regions, pyLoc, maybeIp, inferType);
4364 },
4365 "name"_a, "results"_a = nb::none(), "operands"_a = nb::none(),
4366 "attributes"_a = nb::none(), "successors"_a = nb::none(),
4367 "regions"_a = 0, "loc"_a = nb::none(), "ip"_a = nb::none(),
4368 "infer_type"_a = false,
4369 R"(
4370 Creates a new operation.
4371
4372 Args:
4373 name: Operation name (e.g. `dialect.operation`).
4374 results: Optional sequence of Type representing op result types.
4375 operands: Optional operands of the operation.
4376 attributes: Optional Dict of {str: Attribute}.
4377 successors: Optional List of Block for the operation's successors.
4378 regions: Number of regions to create (default = 0).
4379 location: Optional Location object (defaults to resolve from context manager).
4380 ip: Optional InsertionPoint (defaults to resolve from context manager or set to False to disable insertion, even with an insertion point set in the context manager).
4381 infer_type: Whether to infer result types (default = False).
4382 Returns:
4383 A new detached Operation object. Detached operations can be added to blocks, which causes them to become attached.)")
4384 .def_static(
4385 "parse",
4386 [](const std::string &sourceStr, const std::string &sourceName,
4388 -> nb::typed<nb::object, PyOpView> {
4389 return PyOperation::parse(context->getRef(), sourceStr, sourceName)
4390 ->createOpView();
4391 },
4392 "source"_a, nb::kw_only(), "source_name"_a = "",
4393 "context"_a = nb::none(),
4394 "Parses an operation. Supports both text assembly format and binary "
4395 "bytecode format.")
4397 "Gets a capsule wrapping the MlirOperation.")
4400 "Creates an Operation from a capsule wrapping MlirOperation.")
4401 .def_prop_ro(
4402 "operation",
4403 [](nb::object self) -> nb::typed<nb::object, PyOperation> {
4404 return self;
4405 },
4406 "Returns self (the operation).")
4407 .def_prop_ro(
4408 "opview",
4409 [](PyOperation &self) -> nb::typed<nb::object, PyOpView> {
4410 return self.createOpView();
4411 },
4412 R"(
4413 Returns an OpView of this operation.
4414
4415 Note:
4416 If the operation has a registered and loaded dialect then this OpView will
4417 be concrete wrapper class.)")
4418 .def_prop_ro("block", &PyOperation::getBlock,
4419 "Returns the block containing this operation.")
4420 .def_prop_ro(
4421 "successors",
4422 [](PyOperationBase &self) {
4423 return PyOpSuccessors(self.getOperation().getRef());
4424 },
4425 "Returns the list of Operation successors.")
4426 .def(
4427 "replace_uses_of_with",
4428 [](PyOperation &self, PyValue &of, PyValue &with) {
4429 mlirOperationReplaceUsesOfWith(self.get(), of.get(), with.get());
4430 },
4431 "of"_a, "with_"_a,
4432 "Replaces uses of the 'of' value with the 'with' value inside the "
4433 "operation.")
4434 .def("_set_invalid", &PyOperation::setInvalid,
4435 "Invalidate the operation.");
4436
4437 auto opViewClass =
4438 nb::class_<PyOpView, PyOperationBase>(m, "OpView")
4439 .def(nb::init<nb::typed<nb::object, PyOperation>>(), "operation"_a)
4440 .def(
4441 "__init__",
4442 [](PyOpView *self, std::string_view name,
4443 std::tuple<int, bool> opRegionSpec,
4444 nb::object operandSegmentSpecObj,
4445 nb::object resultSegmentSpecObj,
4446 std::optional<nb::sequence> resultTypeList,
4447 nb::sequence operandList,
4448 std::optional<nb::typed<nb::dict, nb::str, PyAttribute>>
4449 attributes,
4450 std::optional<std::vector<PyBlock *>> successors,
4451 std::optional<int> regions,
4452 const std::optional<PyLocation> &location,
4453 const nb::object &maybeIp) {
4454 PyLocation pyLoc = maybeGetTracebackLocation(location);
4456 name, opRegionSpec, operandSegmentSpecObj,
4457 resultSegmentSpecObj, resultTypeList, operandList,
4458 attributes, successors, regions, pyLoc, maybeIp));
4459 },
4460 "name"_a, "opRegionSpec"_a,
4461 "operandSegmentSpecObj"_a = nb::none(),
4462 "resultSegmentSpecObj"_a = nb::none(), "results"_a = nb::none(),
4463 "operands"_a = nb::none(), "attributes"_a = nb::none(),
4464 "successors"_a = nb::none(), "regions"_a = nb::none(),
4465 "loc"_a = nb::none(), "ip"_a = nb::none())
4466 .def_prop_ro(
4467 "operation",
4468 [](PyOpView &self) -> nb::typed<nb::object, PyOperation> {
4469 return self.getOperationObject();
4470 })
4471 .def_prop_ro("opview",
4472 [](nb::object self) -> nb::typed<nb::object, PyOpView> {
4473 return self;
4474 })
4475 .def(
4476 "__str__",
4477 [](PyOpView &self) { return nb::str(self.getOperationObject()); })
4478 .def_prop_ro(
4479 "successors",
4480 [](PyOperationBase &self) {
4481 return PyOpSuccessors(self.getOperation().getRef());
4482 },
4483 "Returns the list of Operation successors.")
4484 .def(
4485 "_set_invalid",
4486 [](PyOpView &self) { self.getOperation().setInvalid(); },
4487 "Invalidate the operation.");
4488 opViewClass.attr("_ODS_REGIONS") = nb::make_tuple(0, true);
4489 opViewClass.attr("_ODS_OPERAND_SEGMENTS") = nb::none();
4490 opViewClass.attr("_ODS_RESULT_SEGMENTS") = nb::none();
4491 // It is faster to pass the operation_name, ods_regions, and
4492 // ods_operand_segments/ods_result_segments as arguments to the constructor,
4493 // rather than to access them as attributes.
4494 opViewClass.attr("build_generic") = classmethod(
4495 [](nb::handle cls, std::optional<nb::sequence> resultTypeList,
4496 nb::sequence operandList,
4497 std::optional<nb::typed<nb::dict, nb::str, PyAttribute>> attributes,
4498 std::optional<std::vector<PyBlock *>> successors,
4499 std::optional<int> regions, std::optional<PyLocation> location,
4500 const nb::object &maybeIp) {
4501 std::string name = nb::cast<std::string>(cls.attr("OPERATION_NAME"));
4502 std::tuple<int, bool> opRegionSpec =
4503 nb::cast<std::tuple<int, bool>>(cls.attr("_ODS_REGIONS"));
4504 nb::object operandSegmentSpec = cls.attr("_ODS_OPERAND_SEGMENTS");
4505 nb::object resultSegmentSpec = cls.attr("_ODS_RESULT_SEGMENTS");
4506 PyLocation pyLoc = maybeGetTracebackLocation(location);
4507 return PyOpView::buildGeneric(name, opRegionSpec, operandSegmentSpec,
4508 resultSegmentSpec, resultTypeList,
4509 operandList, attributes, successors,
4510 regions, pyLoc, maybeIp);
4511 },
4512 "cls"_a, "results"_a = nb::none(), "operands"_a = nb::none(),
4513 "attributes"_a = nb::none(), "successors"_a = nb::none(),
4514 "regions"_a = nb::none(), "loc"_a = nb::none(), "ip"_a = nb::none(),
4515 // clang-format off
4516 nb::sig("def build_generic(cls, results: Sequence[Type] | None = None, operands: Sequence[Value] | None = None, attributes: dict[str, Attribute] | None = None, successors: Sequence[Block] | None = None, regions: int | None = None, loc: Location | None = None, ip: InsertionPoint | None = None) -> typing.Self"),
4517 // clang-format on
4518 "Builds a specific, generated OpView based on class level attributes.");
4519 opViewClass.attr("parse") = classmethod(
4520 [](const nb::object &cls, const std::string &sourceStr,
4521 const std::string &sourceName,
4522 DefaultingPyMlirContext context) -> nb::typed<nb::object, PyOpView> {
4523 PyOperationRef parsed =
4524 PyOperation::parse(context->getRef(), sourceStr, sourceName);
4525
4526 // Check if the expected operation was parsed, and cast to to the
4527 // appropriate `OpView` subclass if successful.
4528 // NOTE: This accesses attributes that have been automatically added to
4529 // `OpView` subclasses, and is not intended to be used on `OpView`
4530 // directly.
4531 std::string clsOpName =
4532 nb::cast<std::string>(cls.attr("OPERATION_NAME"));
4533 MlirStringRef identifier =
4535 std::string_view parsedOpName(identifier.data, identifier.length);
4536 if (clsOpName != parsedOpName)
4537 throw MLIRError(join("Expected a '", clsOpName, "' op, got: '",
4538 parsedOpName, "'"));
4539 return PyOpView::constructDerived(cls, parsed.getObject());
4540 },
4541 "cls"_a, "source"_a, nb::kw_only(), "source_name"_a = "",
4542 "context"_a = nb::none(),
4543 // clang-format off
4544 nb::sig("def parse(cls, source: str, *, source_name: str = '', context: Context | None = None) -> typing.Self"),
4545 // clang-format on
4546 "Parses a specific, generated OpView based on class level attributes.");
4547 opViewClass.attr("has_trait") = classmethod(
4548 [](nb::object &self, nb::type_object &traitCls,
4549 DefaultingPyMlirContext &context) {
4550 PyTypeID traitTypeID =
4551 nb::cast<PyTypeID>(traitCls.attr(PyDynamicOpTrait::typeIDAttr));
4552 std::string opName = nb::cast<std::string>(self.attr("OPERATION_NAME"));
4554 mlirStringRefCreate(opName.data(), opName.size()),
4555 traitTypeID.get(), context->get());
4556 },
4557 "cls"_a, "trait_cls"_a, "context"_a = nb::none(),
4558 "Checks if the operation has a given trait.");
4559
4561
4562 //----------------------------------------------------------------------------
4563 // Mapping of PyRegion.
4564 //----------------------------------------------------------------------------
4565 nb::class_<PyRegion>(m, "Region")
4566 .def_prop_ro(
4567 "blocks",
4568 [](PyRegion &self) {
4569 return PyBlockList(self.getParentOperation(), self.get());
4570 },
4571 "Returns a forward-optimized sequence of blocks.")
4572 .def_prop_ro(
4573 "owner",
4574 [](PyRegion &self) -> nb::typed<nb::object, PyOpView> {
4575 return self.getParentOperation()->createOpView();
4576 },
4577 "Returns the operation owning this region.")
4578 .def(
4579 "__iter__",
4580 [](PyRegion &self) {
4581 self.checkValid();
4582 MlirBlock firstBlock = mlirRegionGetFirstBlock(self.get());
4583 return PyBlockIterator(self.getParentOperation(), firstBlock);
4584 },
4585 "Iterates over blocks in the region.")
4586 .def(
4587 "__eq__",
4588 [](PyRegion &self, PyRegion &other) {
4589 return self.get().ptr == other.get().ptr;
4590 },
4591 "Compares two regions for pointer equality.")
4592 .def(
4593 "__eq__", [](PyRegion &self, nb::object &other) { return false; },
4594 "Compares region with non-region object (always returns False).");
4595
4596 //----------------------------------------------------------------------------
4597 // Mapping of PyBlock.
4598 //----------------------------------------------------------------------------
4599 nb::class_<PyBlock>(m, "Block")
4601 "Gets a capsule wrapping the MlirBlock.")
4602 .def_prop_ro(
4603 "owner",
4604 [](PyBlock &self) -> nb::typed<nb::object, PyOpView> {
4605 return self.getParentOperation()->createOpView();
4606 },
4607 "Returns the owning operation of this block.")
4608 .def_prop_ro(
4609 "region",
4610 [](PyBlock &self) {
4611 MlirRegion region = mlirBlockGetParentRegion(self.get());
4612 return PyRegion(self.getParentOperation(), region);
4613 },
4614 "Returns the owning region of this block.")
4615 .def_prop_ro(
4616 "arguments",
4617 [](PyBlock &self) {
4618 return PyBlockArgumentList(self.getParentOperation(), self.get());
4619 },
4620 "Returns a list of block arguments.")
4621 .def(
4622 "add_argument",
4623 [](PyBlock &self, const PyType &type, const PyLocation &loc) {
4624 return PyBlockArgument(self.getParentOperation(),
4625 mlirBlockAddArgument(self.get(), type, loc));
4626 },
4627 "type"_a, "loc"_a,
4628 R"(
4629 Appends an argument of the specified type to the block.
4630
4631 Args:
4632 type: The type of the argument to add.
4633 loc: The source location for the argument.
4634
4635 Returns:
4636 The newly added block argument.)")
4637 .def(
4638 "erase_argument",
4639 [](PyBlock &self, unsigned index) {
4640 return mlirBlockEraseArgument(self.get(), index);
4641 },
4642 "index"_a,
4643 R"(
4644 Erases the argument at the specified index.
4645
4646 Args:
4647 index: The index of the argument to erase.)")
4648 .def_prop_ro(
4649 "operations",
4650 [](PyBlock &self) {
4651 return PyOperationList(self.getParentOperation(), self.get());
4652 },
4653 "Returns a forward-optimized sequence of operations.")
4654 .def_static(
4655 "create_at_start",
4656 [](PyRegion &parent, nb::typed<nb::sequence, PyType> pyArgTypes,
4657 const std::optional<nb::typed<nb::sequence, PyLocation>>
4658 &pyArgLocs) {
4659 parent.checkValid();
4660 MlirBlock block = createBlock(pyArgTypes, pyArgLocs);
4661 mlirRegionInsertOwnedBlock(parent, 0, block);
4662 return PyBlock(parent.getParentOperation(), block);
4663 },
4664 "parent"_a, "arg_types"_a = nb::list(), "arg_locs"_a = std::nullopt,
4665 "Creates and returns a new Block at the beginning of the given "
4666 "region (with given argument types and locations).")
4667 .def(
4668 "append_to",
4669 [](PyBlock &self, PyRegion &region) {
4670 MlirBlock b = self.get();
4673 mlirRegionAppendOwnedBlock(region.get(), b);
4674 },
4675 "region"_a,
4676 R"(
4677 Appends this block to a region.
4678
4679 Transfers ownership if the block is currently owned by another region.
4680
4681 Args:
4682 region: The region to append the block to.)")
4683 .def(
4684 "create_before",
4685 [](PyBlock &self, const nb::args &pyArgTypes,
4686 const std::optional<nb::typed<nb::sequence, PyLocation>>
4687 &pyArgLocs) {
4688 self.checkValid();
4689 MlirBlock block =
4690 createBlock(nb::cast<nb::sequence>(pyArgTypes), pyArgLocs);
4691 MlirRegion region = mlirBlockGetParentRegion(self.get());
4692 mlirRegionInsertOwnedBlockBefore(region, self.get(), block);
4693 return PyBlock(self.getParentOperation(), block);
4694 },
4695 "arg_types"_a, nb::kw_only(), "arg_locs"_a = std::nullopt,
4696 "Creates and returns a new Block before this block "
4697 "(with given argument types and locations).")
4698 .def(
4699 "create_after",
4700 [](PyBlock &self, const nb::args &pyArgTypes,
4701 const std::optional<nb::typed<nb::sequence, PyLocation>>
4702 &pyArgLocs) {
4703 self.checkValid();
4704 MlirBlock block =
4705 createBlock(nb::cast<nb::sequence>(pyArgTypes), pyArgLocs);
4706 MlirRegion region = mlirBlockGetParentRegion(self.get());
4707 mlirRegionInsertOwnedBlockAfter(region, self.get(), block);
4708 return PyBlock(self.getParentOperation(), block);
4709 },
4710 "arg_types"_a, nb::kw_only(), "arg_locs"_a = std::nullopt,
4711 "Creates and returns a new Block after this block "
4712 "(with given argument types and locations).")
4713 .def(
4714 "__iter__",
4715 [](PyBlock &self) {
4716 self.checkValid();
4717 MlirOperation firstOperation =
4718 mlirBlockGetFirstOperation(self.get());
4719 return PyOperationIterator(self.getParentOperation(),
4720 firstOperation);
4721 },
4722 "Iterates over operations in the block.")
4723 .def(
4724 "__eq__",
4725 [](PyBlock &self, PyBlock &other) {
4726 return self.get().ptr == other.get().ptr;
4727 },
4728 "Compares two blocks for pointer equality.")
4729 .def(
4730 "__eq__", [](PyBlock &self, nb::object &other) { return false; },
4731 "Compares block with non-block object (always returns False).")
4732 .def(
4733 "__hash__", [](PyBlock &self) { return hash(self.get().ptr); },
4734 "Returns the hash value of the block.")
4735 .def(
4736 "__str__",
4737 [](PyBlock &self) {
4738 self.checkValid();
4739 PyPrintAccumulator printAccum;
4740 mlirBlockPrint(self.get(), printAccum.getCallback(),
4741 printAccum.getUserData());
4742 return printAccum.join();
4743 },
4744 "Returns the assembly form of the block.")
4745 .def(
4746 "append",
4747 [](PyBlock &self, PyOperationBase &operation) {
4748 if (operation.getOperation().isAttached())
4749 operation.getOperation().detachFromParent();
4750
4751 MlirOperation mlirOperation = operation.getOperation().get();
4752 mlirBlockAppendOwnedOperation(self.get(), mlirOperation);
4753 operation.getOperation().setAttached(
4754 self.getParentOperation().getObject());
4755 },
4756 "operation"_a,
4757 R"(
4758 Appends an operation to this block.
4759
4760 If the operation is currently in another block, it will be moved.
4761
4762 Args:
4763 operation: The operation to append to the block.)")
4764 .def_prop_ro(
4765 "successors",
4766 [](PyBlock &self) {
4767 return PyBlockSuccessors(self, self.getParentOperation());
4768 },
4769 "Returns the list of Block successors.")
4770 .def_prop_ro(
4771 "predecessors",
4772 [](PyBlock &self) {
4773 return PyBlockPredecessors(self, self.getParentOperation());
4774 },
4775 "Returns the list of Block predecessors.");
4776
4777 //----------------------------------------------------------------------------
4778 // Mapping of PyInsertionPoint.
4779 //----------------------------------------------------------------------------
4780
4781 nb::class_<PyInsertionPoint>(m, "InsertionPoint")
4782 .def(nb::init<PyBlock &>(), "block"_a,
4783 "Inserts after the last operation but still inside the block.")
4784 .def("__enter__", &PyInsertionPoint::contextEnter,
4785 "Enters the insertion point as a context manager.",
4786 nb::sig("def __enter__(self, /) -> InsertionPoint"))
4787 .def("__exit__", &PyInsertionPoint::contextExit, "exc_type"_a.none(),
4788 "exc_value"_a.none(), "traceback"_a.none(),
4789 "Exits the insertion point context manager.")
4790 .def_prop_ro_static(
4791 "current",
4792 [](nb::object & /*class*/) {
4794 if (!ip)
4795 throw nb::value_error("No current InsertionPoint");
4796 return ip;
4797 },
4798 nb::sig("def current(/) -> InsertionPoint"),
4799 "Gets the InsertionPoint bound to the current thread or raises "
4800 "ValueError if none has been set.")
4801 .def(nb::init<PyOperationBase &>(), "beforeOperation"_a,
4802 "Inserts before a referenced operation.")
4803 .def_static("at_block_begin", &PyInsertionPoint::atBlockBegin, "block"_a,
4804 R"(
4805 Creates an insertion point at the beginning of a block.
4806
4807 Args:
4808 block: The block at whose beginning operations should be inserted.
4809
4810 Returns:
4811 An InsertionPoint at the block's beginning.)")
4812 .def_static("at_block_terminator", &PyInsertionPoint::atBlockTerminator,
4813 "block"_a,
4814 R"(
4815 Creates an insertion point before a block's terminator.
4816
4817 Args:
4818 block: The block whose terminator to insert before.
4819
4820 Returns:
4821 An InsertionPoint before the terminator.
4822
4823 Raises:
4824 ValueError: If the block has no terminator.)")
4825 .def_static("after", &PyInsertionPoint::after, "operation"_a,
4826 R"(
4827 Creates an insertion point immediately after an operation.
4828
4829 Args:
4830 operation: The operation after which to insert.
4831
4832 Returns:
4833 An InsertionPoint after the operation.)")
4834 .def("insert", &PyInsertionPoint::insert, "operation"_a,
4835 R"(
4836 Inserts an operation at this insertion point.
4837
4838 Args:
4839 operation: The operation to insert.)")
4840 .def_prop_ro(
4841 "block", [](PyInsertionPoint &self) { return self.getBlock(); },
4842 "Returns the block that this `InsertionPoint` points to.")
4843 .def_prop_ro(
4844 "ref_operation",
4845 [](PyInsertionPoint &self)
4846 -> std::optional<nb::typed<nb::object, PyOperation>> {
4847 auto refOperation = self.getRefOperation();
4848 if (refOperation)
4849 return refOperation->getObject();
4850 return {};
4851 },
4852 "The reference operation before which new operations are "
4853 "inserted, or None if the insertion point is at the end of "
4854 "the block.");
4855
4856 //----------------------------------------------------------------------------
4857 // Mapping of PyAttribute.
4858 //----------------------------------------------------------------------------
4859 nb::class_<PyAttribute>(m, "Attribute")
4860 // Delegate to the PyAttribute copy constructor, which will also lifetime
4861 // extend the backing context which owns the MlirAttribute.
4862 .def(nb::init<PyAttribute &>(), "cast_from_type"_a,
4863 "Casts the passed attribute to the generic `Attribute`.")
4865 "Gets a capsule wrapping the MlirAttribute.")
4866 .def_static(
4868 "Creates an Attribute from a capsule wrapping `MlirAttribute`.")
4869 .def_static(
4870 "parse",
4871 [](const std::string &attrSpec, DefaultingPyMlirContext context)
4872 -> nb::typed<nb::object, PyAttribute> {
4873 PyMlirContext::ErrorCapture errors(context->getRef());
4874 MlirAttribute attr = mlirAttributeParseGet(
4875 context->get(), toMlirStringRef(attrSpec));
4876 if (mlirAttributeIsNull(attr))
4877 throw MLIRError("Unable to parse attribute", errors.take());
4878 return PyAttribute(context.get()->getRef(), attr).maybeDownCast();
4879 },
4880 "asm"_a, "context"_a = nb::none(),
4881 "Parses an attribute from an assembly form. Raises an `MLIRError` on "
4882 "failure.")
4883 .def_prop_ro(
4884 "context",
4885 [](PyAttribute &self) -> nb::typed<nb::object, PyMlirContext> {
4886 return self.getContext().getObject();
4887 },
4888 "Context that owns the `Attribute`.")
4889 .def_prop_ro(
4890 "type",
4891 [](PyAttribute &self) -> nb::typed<nb::object, PyType> {
4892 return PyType(self.getContext(), mlirAttributeGetType(self))
4893 .maybeDownCast();
4894 },
4895 "Returns the type of the `Attribute`.")
4896 .def(
4897 "get_named",
4898 [](PyAttribute &self, std::string name) {
4899 return PyNamedAttribute(self, std::move(name));
4900 },
4901 nb::keep_alive<0, 1>(),
4902 R"(
4903 Binds a name to the attribute, creating a `NamedAttribute`.
4904
4905 Args:
4906 name: The name to bind to the `Attribute`.
4907
4908 Returns:
4909 A `NamedAttribute` with the given name and this attribute.)")
4910 .def(
4911 "__eq__",
4912 [](PyAttribute &self, PyAttribute &other) { return self == other; },
4913 "Compares two attributes for equality.")
4914 .def(
4915 "__eq__", [](PyAttribute &self, nb::object &other) { return false; },
4916 "Compares attribute with non-attribute object (always returns "
4917 "False).")
4918 .def(
4919 "__hash__", [](PyAttribute &self) { return hash(self.get().ptr); },
4920 "Returns the hash value of the attribute.")
4921 .def(
4922 "dump", [](PyAttribute &self) { mlirAttributeDump(self); },
4924 .def(
4925 "__str__",
4926 [](PyAttribute &self) {
4927 PyPrintAccumulator printAccum;
4928 mlirAttributePrint(self, printAccum.getCallback(),
4929 printAccum.getUserData());
4930 return printAccum.join();
4931 },
4932 "Returns the assembly form of the Attribute.")
4933 .def(
4934 "__repr__",
4935 [](PyAttribute &self) {
4936 // Generally, assembly formats are not printed for __repr__ because
4937 // this can cause exceptionally long debug output and exceptions.
4938 // However, attribute values are generally considered useful and
4939 // are printed. This may need to be re-evaluated if debug dumps end
4940 // up being excessive.
4941 PyPrintAccumulator printAccum;
4942 printAccum.parts.append("Attribute(");
4943 mlirAttributePrint(self, printAccum.getCallback(),
4944 printAccum.getUserData());
4945 printAccum.parts.append(")");
4946 return printAccum.join();
4947 },
4948 "Returns a string representation of the attribute.")
4949 .def_prop_ro(
4950 "typeid",
4951 [](PyAttribute &self) {
4952 MlirTypeID mlirTypeID = mlirAttributeGetTypeID(self);
4953 assert(!mlirTypeIDIsNull(mlirTypeID) &&
4954 "mlirTypeID was expected to be non-null.");
4955 return PyTypeID(mlirTypeID);
4956 },
4957 "Returns the `TypeID` of the attribute.")
4958 .def(
4960 [](PyAttribute &self) -> nb::typed<nb::object, PyAttribute> {
4961 return self.maybeDownCast();
4962 },
4963 "Downcasts the attribute to a more specific attribute if possible.");
4964
4965 //----------------------------------------------------------------------------
4966 // Mapping of PyNamedAttribute
4967 //----------------------------------------------------------------------------
4968 nb::class_<PyNamedAttribute>(m, "NamedAttribute")
4969 .def(
4970 "__repr__",
4971 [](PyNamedAttribute &self) {
4972 PyPrintAccumulator printAccum;
4973 printAccum.parts.append("NamedAttribute(");
4974 printAccum.parts.append(
4975 nb::str(mlirIdentifierStr(self.namedAttr.name).data,
4976 mlirIdentifierStr(self.namedAttr.name).length));
4977 printAccum.parts.append("=");
4978 mlirAttributePrint(self.namedAttr.attribute,
4979 printAccum.getCallback(),
4980 printAccum.getUserData());
4981 printAccum.parts.append(")");
4982 return printAccum.join();
4983 },
4984 "Returns a string representation of the named attribute.")
4985 .def_prop_ro(
4986 "name",
4987 [](PyNamedAttribute &self) {
4988 return mlirIdentifierStr(self.namedAttr.name);
4989 },
4990 "The name of the `NamedAttribute` binding.")
4991 .def_prop_ro(
4992 "attr",
4993 [](PyNamedAttribute &self) { return self.namedAttr.attribute; },
4994 nb::keep_alive<0, 1>(), nb::sig("def attr(self) -> Attribute"),
4995 "The underlying generic attribute of the `NamedAttribute` binding.");
4996
4997 //----------------------------------------------------------------------------
4998 // Mapping of PyType.
4999 //----------------------------------------------------------------------------
5000 nb::class_<PyType>(m, "Type")
5001 // Delegate to the PyType copy constructor, which will also lifetime
5002 // extend the backing context which owns the MlirType.
5003 .def(nb::init<PyType &>(), "cast_from_type"_a,
5004 "Casts the passed type to the generic `Type`.")
5006 "Gets a capsule wrapping the `MlirType`.")
5008 "Creates a Type from a capsule wrapping `MlirType`.")
5009 .def_static(
5010 "parse",
5011 [](std::string typeSpec,
5012 DefaultingPyMlirContext context) -> nb::typed<nb::object, PyType> {
5013 PyMlirContext::ErrorCapture errors(context->getRef());
5014 MlirType type =
5015 mlirTypeParseGet(context->get(), toMlirStringRef(typeSpec));
5016 if (mlirTypeIsNull(type))
5017 throw MLIRError("Unable to parse type", errors.take());
5018 return PyType(context.get()->getRef(), type).maybeDownCast();
5019 },
5020 "asm"_a, "context"_a = nb::none(),
5021 R"(
5022 Parses the assembly form of a type.
5023
5024 Returns a Type object or raises an `MLIRError` if the type cannot be parsed.
5025
5026 See also: https://mlir.llvm.org/docs/LangRef/#type-system)")
5027 .def_prop_ro(
5028 "context",
5029 [](PyType &self) -> nb::typed<nb::object, PyMlirContext> {
5030 return self.getContext().getObject();
5031 },
5032 "Context that owns the `Type`.")
5033 .def(
5034 "__eq__", [](PyType &self, PyType &other) { return self == other; },
5035 "Compares two types for equality.")
5036 .def(
5037 "__eq__", [](PyType &self, nb::object &other) { return false; },
5038 "other"_a.none(),
5039 "Compares type with non-type object (always returns False).")
5040 .def(
5041 "__hash__", [](PyType &self) { return hash(self.get().ptr); },
5042 "Returns the hash value of the `Type`.")
5043 .def(
5044 "dump", [](PyType &self) { mlirTypeDump(self); }, kDumpDocstring)
5045 .def(
5046 "__str__",
5047 [](PyType &self) {
5048 PyPrintAccumulator printAccum;
5049 mlirTypePrint(self, printAccum.getCallback(),
5050 printAccum.getUserData());
5051 return printAccum.join();
5052 },
5053 "Returns the assembly form of the `Type`.")
5054 .def(
5055 "__repr__",
5056 [](PyType &self) {
5057 // Generally, assembly formats are not printed for __repr__ because
5058 // this can cause exceptionally long debug output and exceptions.
5059 // However, types are an exception as they typically have compact
5060 // assembly forms and printing them is useful.
5061 PyPrintAccumulator printAccum;
5062 printAccum.parts.append("Type(");
5063 mlirTypePrint(self, printAccum.getCallback(),
5064 printAccum.getUserData());
5065 printAccum.parts.append(")");
5066 return printAccum.join();
5067 },
5068 "Returns a string representation of the `Type`.")
5069 .def(
5071 [](PyType &self) -> nb::typed<nb::object, PyType> {
5072 return self.maybeDownCast();
5073 },
5074 "Downcasts the Type to a more specific `Type` if possible.")
5075 .def_prop_ro(
5076 "typeid",
5077 [](PyType &self) {
5078 MlirTypeID mlirTypeID = mlirTypeGetTypeID(self);
5079 if (!mlirTypeIDIsNull(mlirTypeID))
5080 return PyTypeID(mlirTypeID);
5081 auto origRepr = nb::cast<std::string>(nb::repr(nb::cast(self)));
5082 throw nb::value_error(join(origRepr, " has no typeid.").c_str());
5083 },
5084 "Returns the `TypeID` of the `Type`, or raises `ValueError` if "
5085 "`Type` has no "
5086 "`TypeID`.");
5087
5088 //----------------------------------------------------------------------------
5089 // Mapping of PyTypeID.
5090 //----------------------------------------------------------------------------
5091 nb::class_<PyTypeID>(m, "TypeID")
5093 "Gets a capsule wrapping the `MlirTypeID`.")
5095 "Creates a `TypeID` from a capsule wrapping `MlirTypeID`.")
5096 // Note, this tests whether the underlying TypeIDs are the same,
5097 // not whether the wrapper MlirTypeIDs are the same, nor whether
5098 // the Python objects are the same (i.e., PyTypeID is a value type).
5099 .def(
5100 "__eq__",
5101 [](PyTypeID &self, PyTypeID &other) { return self == other; },
5102 "Compares two `TypeID`s for equality.")
5103 .def(
5104 "__eq__",
5105 [](PyTypeID &self, const nb::object &other) { return false; },
5106 "Compares TypeID with non-TypeID object (always returns False).")
5107 // Note, this gives the hash value of the underlying TypeID, not the
5108 // hash value of the Python object, nor the hash value of the
5109 // MlirTypeID wrapper.
5110 .def(
5111 "__hash__",
5112 [](PyTypeID &self) {
5113 return static_cast<size_t>(mlirTypeIDHashValue(self));
5114 },
5115 "Returns the hash value of the `TypeID`.");
5116
5117 //----------------------------------------------------------------------------
5118 // Mapping of Value.
5119 //----------------------------------------------------------------------------
5120 m.attr("_T") = nb::type_var("_T", "bound"_a = m.attr("Type"));
5121
5122 nb::class_<PyValue>(m, "Value", nb::is_generic(),
5123 nb::sig("class Value(typing.Generic[_T])"))
5124 .def(nb::init<PyValue &>(), nb::keep_alive<0, 1>(), "value"_a,
5125 "Creates a Value reference from another `Value`.")
5127 "Gets a capsule wrapping the `MlirValue`.")
5129 "Creates a `Value` from a capsule wrapping `MlirValue`.")
5130 .def_prop_ro(
5131 "context",
5132 [](PyValue &self) -> nb::typed<nb::object, PyMlirContext> {
5133 return self.getParentOperation()->getContext().getObject();
5134 },
5135 "Context in which the value lives.")
5136 .def(
5137 "dump", [](PyValue &self) { mlirValueDump(self.get()); },
5139 .def_prop_ro(
5140 "owner",
5141 [](PyValue &self)
5142 -> nb::typed<nb::object, std::variant<PyOpView, PyBlock>> {
5143 MlirValue v = self.get();
5144 if (mlirValueIsAOpResult(v)) {
5145 assert(mlirOperationEqual(self.getParentOperation()->get(),
5146 mlirOpResultGetOwner(self.get())) &&
5147 "expected the owner of the value in Python to match "
5148 "that in "
5149 "the IR");
5150 return self.getParentOperation()->createOpView();
5151 }
5152
5154 MlirBlock block = mlirBlockArgumentGetOwner(self.get());
5155 return nb::cast(PyBlock(self.getParentOperation(), block));
5156 }
5157
5158 assert(false && "Value must be a block argument or an op result");
5159 return nb::none();
5160 },
5161 "Returns the owner of the value (`Operation` for results, `Block` "
5162 "for "
5163 "arguments).")
5164 .def_prop_ro(
5165 "uses",
5166 [](PyValue &self) {
5167 return PyOpOperandIterator(mlirValueGetFirstUse(self.get()));
5168 },
5169 "Returns an iterator over uses of this value.")
5170 .def(
5171 "__eq__",
5172 [](PyValue &self, PyValue &other) {
5173 return self.get().ptr == other.get().ptr;
5174 },
5175 "Compares two values for pointer equality.")
5176 .def(
5177 "__eq__", [](PyValue &self, nb::object other) { return false; },
5178 "Compares value with non-value object (always returns False).")
5179 .def(
5180 "__hash__", [](PyValue &self) { return hash(self.get().ptr); },
5181 "Returns the hash value of the value.")
5182 .def(
5183 "__str__",
5184 [](PyValue &self) {
5185 PyPrintAccumulator printAccum;
5186 printAccum.parts.append("Value(");
5187 mlirValuePrint(self.get(), printAccum.getCallback(),
5188 printAccum.getUserData());
5189 printAccum.parts.append(")");
5190 return printAccum.join();
5191 },
5192 R"(
5193 Returns the string form of the value.
5194
5195 If the value is a block argument, this is the assembly form of its type and the
5196 position in the argument list. If the value is an operation result, this is
5197 equivalent to printing the operation that produced it.
5198 )")
5199 .def(
5200 "get_name",
5201 [](PyValue &self, bool useLocalScope, bool useNameLocAsPrefix) {
5202 PyPrintAccumulator printAccum;
5203 MlirOpPrintingFlags flags = mlirOpPrintingFlagsCreate();
5204 if (useLocalScope)
5206 if (useNameLocAsPrefix)
5208 MlirAsmState valueState =
5209 mlirAsmStateCreateForValue(self.get(), flags);
5210 mlirValuePrintAsOperand(self.get(), valueState,
5211 printAccum.getCallback(),
5212 printAccum.getUserData());
5214 mlirAsmStateDestroy(valueState);
5215 return printAccum.join();
5216 },
5217 "use_local_scope"_a = false, "use_name_loc_as_prefix"_a = false,
5218 R"(
5219 Returns the string form of value as an operand.
5220
5221 Args:
5222 use_local_scope: Whether to use local scope for naming.
5223 use_name_loc_as_prefix: Whether to use the location attribute (NameLoc) as prefix.
5224
5225 Returns:
5226 The value's name as it appears in IR (e.g., `%0`, `%arg0`).)")
5227 .def(
5228 "get_name",
5229 [](PyValue &self, PyAsmState &state) {
5230 PyPrintAccumulator printAccum;
5231 MlirAsmState valueState = state.get();
5232 mlirValuePrintAsOperand(self.get(), valueState,
5233 printAccum.getCallback(),
5234 printAccum.getUserData());
5235 return printAccum.join();
5236 },
5237 "state"_a,
5238 "Returns the string form of value as an operand (i.e., the ValueID).")
5239 .def_prop_ro(
5240 "type",
5241 [](PyValue &self) {
5242 return PyType(self.getParentOperation()->getContext(),
5243 mlirValueGetType(self.get()))
5244 .maybeDownCast();
5245 },
5246 "Returns the type of the value.", nb::sig("def type(self) -> _T"))
5247 .def(
5248 "set_type",
5249 [](PyValue &self, const PyType &type) {
5250 mlirValueSetType(self.get(), type);
5251 },
5252 "type"_a, "Sets the type of the value.",
5253 nb::sig("def set_type(self, type: _T)"))
5254 .def(
5255 "replace_all_uses_with",
5256 [](PyValue &self, PyValue &with) {
5257 mlirValueReplaceAllUsesOfWith(self.get(), with.get());
5258 },
5259 "Replace all uses of value with the new value, updating anything in "
5260 "the IR that uses `self` to use the other value instead.")
5261 .def(
5262 "replace_all_uses_except",
5263 [](PyValue &self, PyValue &with, PyOperation &exception) {
5264 MlirOperation exceptedUser = exception.get();
5265 mlirValueReplaceAllUsesExcept(self, with, 1, &exceptedUser);
5266 },
5267 "with_"_a, "exceptions"_a, kValueReplaceAllUsesExceptDocstring)
5268 .def(
5269 "replace_all_uses_except",
5270 [](PyValue &self, PyValue &with,
5271 std::vector<PyOperation> &exceptions) {
5272 // Convert Python list to a std::vector of MlirOperations
5273 std::vector<MlirOperation> exceptionOps;
5274 for (PyOperation &exception : exceptions)
5275 exceptionOps.push_back(exception);
5277 self, with, static_cast<intptr_t>(exceptionOps.size()),
5278 exceptionOps.data());
5279 },
5280 "with_"_a, "exceptions"_a, kValueReplaceAllUsesExceptDocstring)
5281 .def(
5283 [](PyValue &self) { return self.maybeDownCast(); },
5284 "Downcasts the `Value` to a more specific kind if possible.")
5285 .def_prop_ro(
5286 "location",
5287 [](PyValue self) {
5288 return PyLocation(
5291 .maybeDownCast();
5292 },
5293 "Returns the source location of the value.");
5294
5298
5299 nb::class_<PyAsmState>(m, "AsmState")
5300 .def(nb::init<PyValue &, bool>(), "value"_a, "use_local_scope"_a = false,
5301 R"(
5302 Creates an `AsmState` for consistent SSA value naming.
5303
5304 Args:
5305 value: The value to create state for.
5306 use_local_scope: Whether to use local scope for naming.)")
5307 .def(nb::init<PyOperationBase &, bool>(), "op"_a,
5308 "use_local_scope"_a = false,
5309 R"(
5310 Creates an AsmState for consistent SSA value naming.
5311
5312 Args:
5313 op: The operation to create state for.
5314 use_local_scope: Whether to use local scope for naming.)");
5315
5316 //----------------------------------------------------------------------------
5317 // Mapping of SymbolTable.
5318 //----------------------------------------------------------------------------
5319 nb::class_<PySymbolTable>(m, "SymbolTable")
5320 .def(nb::init<PyOperationBase &>(),
5321 R"(
5322 Creates a symbol table for an operation.
5323
5324 Args:
5325 operation: The `Operation` that defines a symbol table (e.g., a `ModuleOp`).
5326
5327 Raises:
5328 TypeError: If the operation is not a symbol table.)")
5329 .def(
5330 "__getitem__",
5331 [](PySymbolTable &self,
5332 const std::string &name) -> nb::typed<nb::object, PyOpView> {
5333 return self.dunderGetItem(name);
5334 },
5335 R"(
5336 Looks up a symbol by name in the symbol table.
5337
5338 Args:
5339 name: The name of the symbol to look up.
5340
5341 Returns:
5342 The operation defining the symbol.
5343
5344 Raises:
5345 KeyError: If the symbol is not found.)")
5346 .def("insert", &PySymbolTable::insert, "operation"_a,
5347 R"(
5348 Inserts a symbol operation into the symbol table.
5349
5350 Args:
5351 operation: An operation with a symbol name to insert.
5352
5353 Returns:
5354 The symbol name attribute of the inserted operation.
5355
5356 Raises:
5357 ValueError: If the operation does not have a symbol name.)")
5358 .def("erase", &PySymbolTable::erase, "operation"_a,
5359 R"(
5360 Erases a symbol operation from the symbol table.
5361
5362 Args:
5363 operation: The symbol operation to erase.
5364
5365 Note:
5366 The operation is also erased from the IR and invalidated.)")
5367 .def("__delitem__", &PySymbolTable::dunderDel,
5368 "Deletes a symbol by name from the symbol table.")
5369 .def(
5370 "__contains__",
5371 [](PySymbolTable &table, const std::string &name) {
5372 return !mlirOperationIsNull(mlirSymbolTableLookup(
5373 table, mlirStringRefCreate(name.data(), name.length())));
5374 },
5375 "Checks if a symbol with the given name exists in the table.")
5376 // Static helpers.
5377 .def_static("set_symbol_name", &PySymbolTable::setSymbolName, "symbol"_a,
5378 "name"_a, "Sets the symbol name for a symbol operation.")
5379 .def_static("get_symbol_name", &PySymbolTable::getSymbolName, "symbol"_a,
5380 "Gets the symbol name from a symbol operation.")
5381 .def_static("get_visibility", &PySymbolTable::getVisibility, "symbol"_a,
5382 "Gets the visibility attribute of a symbol operation.")
5383 .def_static("set_visibility", &PySymbolTable::setVisibility, "symbol"_a,
5384 "visibility"_a,
5385 "Sets the visibility attribute of a symbol operation.")
5386 .def_static("replace_all_symbol_uses",
5387 &PySymbolTable::replaceAllSymbolUses, "old_symbol"_a,
5388 "new_symbol"_a, "from_op"_a,
5389 "Replaces all uses of a symbol with a new symbol name within "
5390 "the given operation.")
5391 .def_static("walk_symbol_tables", &PySymbolTable::walkSymbolTables,
5392 "from_op"_a, "all_sym_uses_visible"_a, "callback"_a,
5393 "Walks symbol tables starting from an operation with a "
5394 "callback function.");
5395
5396 // Container bindings.
5411
5412 // Debug bindings.
5414
5415 // Attribute builder getter.
5417
5418 // Extensible Dialect
5424
5425 // MLIRError exception.
5426 MLIRError::bind(m);
5427
5428 // Register an atexit handler to clear the thread-local context stack.
5429 // The stack holds nb::object references that prevent Python GC of Contexts.
5430 // At interpreter shutdown, thread_local storage outlives Py_Finalize() on
5431 // the main thread. When the thread_local vector destructs, its nb::object
5432 // members call Py_DECREF through the dead runtime, causing a segfault.
5433 // Clearing the stack in atexit releases references while alive.
5434 nb::module_::import_("atexit").attr("register")(
5435 nb::cpp_function([]() { PyThreadContextEntry::getStack().clear(); }));
5436}
5437} // namespace MLIR_BINDINGS_PYTHON_DOMAIN
5438} // namespace python
5439} // namespace mlir
return success()
void mlirSetGlobalDebugTypes(const char **types, intptr_t n)
Definition Debug.cpp:28
MLIR_CAPI_EXPORTED void mlirSetGlobalDebugType(const char *type)
Sets the current debug type, similarly to -debug-only=type in the command-line tools.
Definition Debug.cpp:20
MLIR_CAPI_EXPORTED bool mlirIsGlobalDebugEnabled()
Retuns true if the global debugging flag is set, false otherwise.
Definition Debug.cpp:18
MLIR_CAPI_EXPORTED void mlirEnableGlobalDebug(bool enable)
Sets the global debugging flag.
Definition Debug.cpp:16
static const char kDumpDocstring[]
Definition IRAffine.cpp:32
static const char kModuleParseDocstring[]
Definition IRCore.cpp:33
static size_t hash(const T &value)
Local helper to compute std::hash for a value.
Definition IRCore.cpp:56
static nb::object createCustomDialectWrapper(const std::string &dialectNamespace, nb::object dialectDescriptor)
Definition IRCore.cpp:61
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
static const char kValueReplaceAllUsesExceptDocstring[]
Definition IRCore.cpp:44
MlirContext mlirModuleGetContext(MlirModule module)
Definition IR.cpp:471
size_t mlirModuleHashValue(MlirModule mod)
Definition IR.cpp:497
intptr_t mlirBlockGetNumPredecessors(MlirBlock block)
Definition IR.cpp:1150
MlirIdentifier mlirOperationGetName(MlirOperation op)
Definition IR.cpp:719
bool mlirValueIsABlockArgument(MlirValue value)
Definition IR.cpp:1170
intptr_t mlirOperationGetNumRegions(MlirOperation op)
Definition IR.cpp:731
MlirBlock mlirOperationGetBlock(MlirOperation op)
Definition IR.cpp:723
void mlirBlockArgumentSetType(MlirValue value, MlirType type)
Definition IR.cpp:1187
void mlirOperationStateAddAttributes(MlirOperationState *state, intptr_t n, MlirNamedAttribute const *attributes)
Definition IR.cpp:546
MlirValue mlirOperationGetResult(MlirOperation op, intptr_t pos)
Definition IR.cpp:786
MlirModule mlirModuleCreateParseFromFile(MlirContext context, MlirStringRef fileName)
Definition IR.cpp:462
bool mlirOperationNameHasTrait(MlirStringRef opName, MlirTypeID traitTypeID, MlirContext context)
Definition IR.cpp:699
MlirAsmState mlirAsmStateCreateForValue(MlirValue value, MlirOpPrintingFlags flags)
Definition IR.cpp:195
intptr_t mlirOperationGetNumResults(MlirOperation op)
Definition IR.cpp:782
void mlirOperationDestroy(MlirOperation op)
Definition IR.cpp:664
MlirContext mlirAttributeGetContext(MlirAttribute attribute)
Definition IR.cpp:1345
MlirType mlirValueGetType(MlirValue value)
Definition IR.cpp:1206
void mlirBlockPrint(MlirBlock block, MlirStringCallback callback, void *userData)
Definition IR.cpp:1136
MlirOpPrintingFlags mlirOpPrintingFlagsCreate()
Definition IR.cpp:219
bool mlirModuleEqual(MlirModule lhs, MlirModule rhs)
Definition IR.cpp:493
void mlirOpPrintingFlagsElideLargeElementsAttrs(MlirOpPrintingFlags flags, intptr_t largeElementLimit)
Definition IR.cpp:227
void mlirOperationSetSuccessor(MlirOperation op, intptr_t pos, MlirBlock block)
Definition IR.cpp:847
MlirOperation mlirOperationGetNextInBlock(MlirOperation op)
Definition IR.cpp:755
void mlirOpPrintingFlagsEnableDebugInfo(MlirOpPrintingFlags flags, bool enable, bool prettyForm)
Definition IR.cpp:237
MlirOperation mlirModuleGetOperation(MlirModule module)
Definition IR.cpp:485
void mlirOpPrintingFlagsElideLargeResourceString(MlirOpPrintingFlags flags, intptr_t largeResourceLimit)
Definition IR.cpp:232
void mlirOpPrintingFlagsUseLocalScope(MlirOpPrintingFlags flags)
Definition IR.cpp:250
intptr_t mlirBlockArgumentGetArgNumber(MlirValue value)
Definition IR.cpp:1182
MlirBlock mlirOperationGetSuccessor(MlirOperation op, intptr_t pos)
Definition IR.cpp:794
bool mlirAttributeEqual(MlirAttribute a1, MlirAttribute a2)
Definition IR.cpp:1364
MlirAsmState mlirAsmStateCreateForOperation(MlirOperation op, MlirOpPrintingFlags flags)
Definition IR.cpp:174
bool mlirOperationEqual(MlirOperation op, MlirOperation other)
Definition IR.cpp:668
void mlirOpPrintingFlagsAssumeVerified(MlirOpPrintingFlags flags)
Definition IR.cpp:254
void mlirBytecodeWriterConfigDestroy(MlirBytecodeWriterConfig config)
Definition IR.cpp:269
MlirBlock mlirBlockGetSuccessor(MlirBlock block, intptr_t pos)
Definition IR.cpp:1146
void mlirModuleDestroy(MlirModule module)
Definition IR.cpp:479
MlirModule mlirModuleCreateEmpty(MlirLocation location)
Definition IR.cpp:450
void mlirOpPrintingFlagsPrintGenericOpForm(MlirOpPrintingFlags flags)
Definition IR.cpp:242
MlirOperation mlirOperationGetParentOperation(MlirOperation op)
Definition IR.cpp:727
void mlirValueSetType(MlirValue value, MlirType type)
Definition IR.cpp:1210
intptr_t mlirOperationGetNumSuccessors(MlirOperation op)
Definition IR.cpp:790
MlirDialect mlirAttributeGetDialect(MlirAttribute attr)
Definition IR.cpp:1360
void mlirLocationPrint(MlirLocation location, MlirStringCallback callback, void *userData)
Definition IR.cpp:440
void mlirOperationSetAttributeByName(MlirOperation op, MlirStringRef name, MlirAttribute attr)
Definition IR.cpp:866
void mlirOperationSetOperand(MlirOperation op, intptr_t pos, MlirValue newValue)
Definition IR.cpp:771
MlirOperation mlirOpResultGetOwner(MlirValue value)
Definition IR.cpp:1197
MlirModule mlirModuleCreateParse(MlirContext context, MlirStringRef module)
Definition IR.cpp:454
size_t mlirOperationHashValue(MlirOperation op)
Definition IR.cpp:672
void mlirOperationStateAddResults(MlirOperationState *state, intptr_t n, MlirType const *results)
Definition IR.cpp:529
MlirOperation mlirOperationClone(MlirOperation op)
Definition IR.cpp:660
MlirBlock mlirBlockArgumentGetOwner(MlirValue value)
Definition IR.cpp:1178
void mlirBlockArgumentSetLocation(MlirValue value, MlirLocation loc)
Definition IR.cpp:1192
MlirValue mlirOperationGetOperand(MlirOperation op, intptr_t pos)
Definition IR.cpp:763
MlirOpOperand mlirOperationGetOpOperand(MlirOperation op, intptr_t pos)
Definition IR.cpp:767
MlirLocation mlirOperationGetLocation(MlirOperation op)
Definition IR.cpp:705
MlirAttribute mlirOperationGetAttributeByName(MlirOperation op, MlirStringRef name)
Definition IR.cpp:861
MlirTypeID mlirAttributeGetTypeID(MlirAttribute attr)
Definition IR.cpp:1356
void mlirOperationStateAddOwnedRegions(MlirOperationState *state, intptr_t n, MlirRegion const *regions)
Definition IR.cpp:538
void mlirOperationSetLocation(MlirOperation op, MlirLocation loc)
Definition IR.cpp:709
MlirType mlirAttributeGetType(MlirAttribute attribute)
Definition IR.cpp:1349
bool mlirOperationRemoveAttributeByName(MlirOperation op, MlirStringRef name)
Definition IR.cpp:871
bool mlirValueIsAOpResult(MlirValue value)
Definition IR.cpp:1174
MlirBlock mlirBlockGetPredecessor(MlirBlock block, intptr_t pos)
Definition IR.cpp:1155
size_t mlirOperationStructuralHashValue(MlirOperation op, uint32_t flags)
Definition IR.cpp:688
MlirRegion mlirOperationGetRegion(MlirOperation op, intptr_t pos)
Definition IR.cpp:735
MlirOperation mlirOperationCreate(MlirOperationState *state)
Definition IR.cpp:614
bool mlirOperationIsStructurallyEquivalent(MlirOperation lhs, MlirOperation rhs, uint32_t flags)
Definition IR.cpp:682
void mlirBytecodeWriterConfigDesiredEmitVersion(MlirBytecodeWriterConfig flags, int64_t version)
Definition IR.cpp:273
MlirAttribute mlirAttributeParseGet(MlirContext context, MlirStringRef attr)
Definition IR.cpp:1341
void mlirOperationRemoveFromParent(MlirOperation op)
Definition IR.cpp:666
intptr_t mlirBlockGetNumSuccessors(MlirBlock block)
Definition IR.cpp:1142
MlirNamedAttribute mlirOperationGetAttribute(MlirOperation op, intptr_t pos)
Definition IR.cpp:856
void mlirOpPrintingFlagsDestroy(MlirOpPrintingFlags flags)
Definition IR.cpp:223
void mlirValueDump(MlirValue value)
Definition IR.cpp:1214
void mlirTypePrint(MlirType type, MlirStringCallback callback, void *userData)
Definition IR.cpp:1330
MlirBlock mlirModuleGetBody(MlirModule module)
Definition IR.cpp:475
MlirOperation mlirOperationCreateParse(MlirContext context, MlirStringRef sourceStr, MlirStringRef sourceName)
Definition IR.cpp:651
void mlirAsmStateDestroy(MlirAsmState state)
Destroys printing flags created with mlirAsmStateCreate.
Definition IR.cpp:213
MlirContext mlirOperationGetContext(MlirOperation op)
Definition IR.cpp:695
intptr_t mlirOpResultGetResultNumber(MlirValue value)
Definition IR.cpp:1201
void mlirOperationStateAddSuccessors(MlirOperationState *state, intptr_t n, MlirBlock const *successors)
Definition IR.cpp:542
MlirBytecodeWriterConfig mlirBytecodeWriterConfigCreate()
Definition IR.cpp:265
void mlirOpPrintingFlagsPrintNameLocAsPrefix(MlirOpPrintingFlags flags)
Definition IR.cpp:246
void mlirOpPrintingFlagsSkipRegions(MlirOpPrintingFlags flags)
Definition IR.cpp:258
void mlirOperationStateAddOperands(MlirOperationState *state, intptr_t n, MlirValue const *operands)
Definition IR.cpp:534
MlirOperationState mlirOperationStateGet(MlirStringRef name, MlirLocation loc)
Definition IR.cpp:505
intptr_t mlirOperationGetNumOperands(MlirOperation op)
Definition IR.cpp:759
void mlirTypeDump(MlirType type)
Definition IR.cpp:1335
intptr_t mlirOperationGetNumAttributes(MlirOperation op)
Definition IR.cpp:852
static PyObject * mlirPythonTypeIDToCapsule(MlirTypeID typeID)
Creates a capsule object encapsulating the raw C-API MlirTypeID.
Definition Interop.h:348
static PyObject * mlirPythonContextToCapsule(MlirContext context)
Creates a capsule object encapsulating the raw C-API MlirContext.
Definition Interop.h:216
#define MLIR_PYTHON_MAYBE_DOWNCAST_ATTR
Attribute on MLIR Python objects that expose a function for downcasting the corresponding Python obje...
Definition Interop.h:118
static MlirOperation mlirPythonCapsuleToOperation(PyObject *capsule)
Extracts an MlirOperations from a capsule as produced from mlirPythonOperationToCapsule.
Definition Interop.h:338
#define MLIR_PYTHON_CAPI_PTR_ATTR
Attribute on MLIR Python objects that expose their C-API pointer.
Definition Interop.h:97
static MlirAttribute mlirPythonCapsuleToAttribute(PyObject *capsule)
Extracts an MlirAttribute from a capsule as produced from mlirPythonAttributeToCapsule.
Definition Interop.h:189
static PyObject * mlirPythonTypeToCapsule(MlirType type)
Creates a capsule object encapsulating the raw C-API MlirType.
Definition Interop.h:367
static PyObject * mlirPythonOperationToCapsule(MlirOperation operation)
Creates a capsule object encapsulating the raw C-API MlirOperation.
Definition Interop.h:330
static PyObject * mlirPythonAttributeToCapsule(MlirAttribute attribute)
Creates a capsule object encapsulating the raw C-API MlirAttribute.
Definition Interop.h:180
#define MLIR_PYTHON_CAPI_FACTORY_ATTR
Attribute on MLIR Python objects that exposes a factory function for constructing the corresponding P...
Definition Interop.h:110
static MlirModule mlirPythonCapsuleToModule(PyObject *capsule)
Extracts an MlirModule from a capsule as produced from mlirPythonModuleToCapsule.
Definition Interop.h:282
static MlirContext mlirPythonCapsuleToContext(PyObject *capsule)
Extracts a MlirContext from a capsule as produced from mlirPythonContextToCapsule.
Definition Interop.h:224
static MlirTypeID mlirPythonCapsuleToTypeID(PyObject *capsule)
Extracts an MlirTypeID from a capsule as produced from mlirPythonTypeIDToCapsule.
Definition Interop.h:357
#define MLIR_PYTHON_CAPI_VALUE_CASTER_REGISTER_ATTR
Attribute on main C extension module (_mlir) that corresponds to the value caster registration bindin...
Definition Interop.h:142
static PyObject * mlirPythonBlockToCapsule(MlirBlock block)
Creates a capsule object encapsulating the raw C-API MlirBlock.
Definition Interop.h:198
static PyObject * mlirPythonLocationToCapsule(MlirLocation loc)
Creates a capsule object encapsulating the raw C-API MlirLocation.
Definition Interop.h:255
static MlirDialectRegistry mlirPythonCapsuleToDialectRegistry(PyObject *capsule)
Extracts an MlirDialectRegistry from a capsule as produced from mlirPythonDialectRegistryToCapsule.
Definition Interop.h:245
static MlirType mlirPythonCapsuleToType(PyObject *capsule)
Extracts an MlirType from a capsule as produced from mlirPythonTypeToCapsule.
Definition Interop.h:376
static MlirValue mlirPythonCapsuleToValue(PyObject *capsule)
Extracts an MlirValue from a capsule as produced from mlirPythonValueToCapsule.
Definition Interop.h:454
static PyObject * mlirPythonValueToCapsule(MlirValue value)
Creates a capsule object encapsulating the raw C-API MlirValue.
Definition Interop.h:445
static PyObject * mlirPythonModuleToCapsule(MlirModule module)
Creates a capsule object encapsulating the raw C-API MlirModule.
Definition Interop.h:273
static MlirLocation mlirPythonCapsuleToLocation(PyObject *capsule)
Extracts an MlirLocation from a capsule as produced from mlirPythonLocationToCapsule.
Definition Interop.h:264
#define MLIR_PYTHON_CAPI_TYPE_CASTER_REGISTER_ATTR
Attribute on main C extension module (_mlir) that corresponds to the type caster registration binding...
Definition Interop.h:130
static PyObject * mlirPythonDialectRegistryToCapsule(MlirDialectRegistry registry)
Creates a capsule object encapsulating the raw C-API MlirDialectRegistry.
Definition Interop.h:235
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static std::string diag(const llvm::Value &value)
Accumulates into a file, either writing text (default) or binary.
A CRTP base class for pseudo-containers willing to support Python-type slicing access on top of index...
Sliceable(intptr_t startIndex, intptr_t length, intptr_t step)
ReferrentTy * get() const
PyMlirContextRef & getContext()
Accesses the context reference.
Definition IRCore.h:310
Used in function arguments when None should resolve to the current context manager set instance.
Definition IRCore.h:291
PyAsmState(MlirValue value, bool useLocalScope)
Definition IRCore.cpp:1750
Wrapper around the generic MlirAttribute.
Definition IRCore.h:1028
PyAttribute(PyMlirContextRef contextRef, MlirAttribute attr)
Definition IRCore.h:1030
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirAttribute.
Definition IRCore.cpp:1860
bool operator==(const PyAttribute &other) const
Definition IRCore.cpp:1856
static PyAttribute createFromCapsule(const nanobind::object &capsule)
Creates a PyAttribute from the MlirAttribute wrapped by a capsule.
Definition IRCore.cpp:1864
nanobind::typed< nanobind::object, PyAttribute > maybeDownCast()
Definition IRCore.cpp:1872
PyBlockArgumentList(PyOperationRef operation, MlirBlock block, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2206
Python wrapper for MlirBlockArgument.
Definition IRCore.h:1768
nanobind::typed< nanobind::object, PyBlock > dunderNext()
Definition IRCore.cpp:217
Blocks are exposed by the C-API as a forward-only linked list.
Definition IRCore.h:1563
PyBlock appendBlock(const nanobind::args &pyArgTypes, const std::optional< nanobind::sequence > &pyArgLocs)
Definition IRCore.cpp:273
PyBlockPredecessors(PyBlock block, PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2366
PyBlockSuccessors(PyBlock block, PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2343
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirBlock.
Definition IRCore.cpp:186
Represents a diagnostic handler attached to the context.
Definition IRCore.h:432
void detach()
Detaches the handler. Does nothing if not attached.
Definition IRCore.cpp:728
PyDiagnosticHandler(MlirContext context, nanobind::object callback)
Definition IRCore.cpp:722
void contextExit(const nanobind::object &excType, const nanobind::object &excVal, const nanobind::object &excTb)
Definition IRCore.h:444
Python class mirroring the C MlirDiagnostic struct.
Definition IRCore.h:382
nanobind::typed< nanobind::object, PyLocation > getLocation()
Definition IRCore.cpp:752
nanobind::typed< nanobind::tuple, PyDiagnostic > getNotes()
Definition IRCore.cpp:767
Wrapper around an MlirDialectRegistry.
Definition IRCore.h:524
static PyDialectRegistry createFromCapsule(nanobind::object capsule)
Definition IRCore.cpp:812
User-level object for accessing dialects with dotted syntax such as: ctx.dialect.std.
Definition IRCore.h:500
MlirDialect getDialectForKey(const std::string &key, bool attrError)
Definition IRCore.cpp:795
static bool attach(const nanobind::object &opName, const nanobind::object &target, PyMlirContext &context)
Definition IRCore.cpp:2571
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2671
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2625
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2648
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2696
CurrentLocAction
Policy for composing Location.current with the computed location.
Definition Globals.h:141
OnExplicitAction
Policy for handling explicit loc= when loc_tracebacks() is active.
Definition Globals.h:133
Globals that are always accessible once the extension has been initialized.
Definition Globals.h:29
void registerOpAdaptorImpl(const std::string &operationName, nanobind::object pyClass, bool replace=false)
Adds an operation adaptor class.
Definition Globals.cpp:167
std::optional< nanobind::callable > lookupValueCaster(MlirTypeID mlirTypeID, MlirDialect dialect)
Returns the custom value caster for MlirTypeID mlirTypeID.
Definition Globals.cpp:203
bool loadDialectModule(std::string_view dialectNamespace)
Loads a python module corresponding to the given dialect namespace.
Definition Globals.cpp:64
void registerDialectImpl(const std::string &dialectNamespace, nanobind::object pyClass, bool replace=false)
Adds a concrete implementation dialect class.
Definition Globals.cpp:145
static PyGlobals & get()
Most code should get the globals via this static accessor.
Definition Globals.cpp:59
std::optional< nanobind::object > lookupOperationClass(std::string_view operationName)
Looks up a registered operation class (deriving from OpView) by operation name.
Definition Globals.cpp:233
void registerTypeCaster(MlirTypeID mlirTypeID, nanobind::callable typeCaster, bool replace=false)
Adds a user-friendly type caster.
Definition Globals.cpp:125
void registerOperationImpl(const std::string &operationName, nanobind::object pyClass, bool replace=false)
Adds a concrete implementation operation class.
Definition Globals.cpp:156
void setDialectSearchPrefixes(std::vector< std::string > newValues)
Definition Globals.h:43
std::optional< nanobind::callable > lookupTypeCaster(MlirTypeID mlirTypeID, MlirDialect dialect)
Returns the custom type caster for MlirTypeID mlirTypeID.
Definition Globals.cpp:189
void registerValueCaster(MlirTypeID mlirTypeID, nanobind::callable valueCaster, bool replace=false)
Adds a user-friendly value caster.
Definition Globals.cpp:135
std::optional< nanobind::callable > lookupAttributeBuilder(const std::string &attributeKind)
Returns the custom Attribute builder for Attribute kind.
Definition Globals.cpp:179
std::optional< nanobind::object > lookupDialectClass(const std::string &dialectNamespace)
Looks up a registered dialect class by namespace.
Definition Globals.cpp:218
std::vector< std::string > getDialectSearchPrefixes()
Get and set the list of parent modules to search for dialect implementation classes.
Definition Globals.h:39
void registerAttributeBuilder(const std::string &attributeKind, nanobind::callable pyFunc, bool replace=false, bool allow_existing=false)
Adds a user-friendly Attribute builder.
Definition Globals.cpp:99
An insertion point maintains a pointer to a Block and a reference operation.
Definition IRCore.h:859
void insert(PyOperationBase &operationBase)
Inserts an operation.
Definition IRCore.cpp:1781
void contextExit(const nanobind::object &excType, const nanobind::object &excVal, const nanobind::object &excTb)
Definition IRCore.cpp:1846
static PyInsertionPoint atBlockTerminator(PyBlock &block)
Shortcut to create an insertion point before the block terminator.
Definition IRCore.cpp:1820
static PyInsertionPoint after(PyOperationBase &op)
Shortcut to create an insertion point to the node after the specified operation.
Definition IRCore.cpp:1829
static PyInsertionPoint atBlockBegin(PyBlock &block)
Shortcut to create an insertion point at the beginning of the block.
Definition IRCore.cpp:1807
PyInsertionPoint(const PyBlock &block)
Creates an insertion point positioned after the last operation in the block, but still inside the blo...
Definition IRCore.cpp:1772
static nanobind::object contextEnter(nanobind::object insertionPoint)
Enter and exit the context manager.
Definition IRCore.cpp:1842
static nanobind::object contextEnter(nanobind::object location)
Enter and exit the context manager.
Definition IRCore.cpp:836
static PyLocation createFromCapsule(nanobind::object capsule)
Creates a PyLocation from the MlirLocation wrapped by a capsule.
Definition IRCore.cpp:828
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirLocation.
Definition IRCore.cpp:824
nanobind::typed< nanobind::object, PyLocation > maybeDownCast()
Returns the most-derived Location subclass registered for this TypeID, or self.
Definition IRCore.cpp:1890
void contextExit(const nanobind::object &excType, const nanobind::object &excVal, const nanobind::object &excTb)
Definition IRCore.cpp:840
PyLocation(PyMlirContextRef contextRef, MlirLocation loc)
Definition IRCore.h:319
static PyMlirContextRef forContext(MlirContext context)
Returns a context reference for the singleton PyMlirContext wrapper for the given context.
Definition IRCore.cpp:461
static size_t getLiveCount()
Gets the count of live context objects. Used for testing.
Definition IRCore.cpp:486
static nanobind::object createFromCapsule(nanobind::object capsule)
Creates a PyMlirContext from the MlirContext wrapped by a capsule.
Definition IRCore.cpp:454
nanobind::object attachDiagnosticHandler(nanobind::object callback)
Attaches a Python callback as a diagnostic handler, returning a registration object (internally a PyD...
Definition IRCore.cpp:501
void contextExit(const nanobind::object &excType, const nanobind::object &excVal, const nanobind::object &excTb)
Definition IRCore.cpp:495
MlirContext get()
Accesses the underlying MlirContext.
Definition IRCore.h:224
PyMlirContextRef getRef()
Gets a strong reference to this context, which will ensure it is kept alive for the life of the refer...
Definition IRCore.cpp:446
void setEmitErrorDiagnostics(bool value)
Controls whether error diagnostics should be propagated to diagnostic handlers, instead of being capt...
Definition IRCore.h:258
static nanobind::object contextEnter(nanobind::object context)
Enter and exit the context manager.
Definition IRCore.cpp:491
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirContext.
Definition IRCore.cpp:450
size_t getLiveModuleCount()
Gets the count of live modules associated with this context.
Definition IRCore.cpp:1840
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirModule.
Definition IRCore.cpp:905
MlirModule get()
Gets the backing MlirModule.
Definition IRCore.h:574
static PyModuleRef forModule(MlirModule module)
Returns a PyModule reference for the given MlirModule.
Definition IRCore.cpp:873
static nanobind::object createFromCapsule(nanobind::object capsule)
Creates a PyModule from the MlirModule wrapped by a capsule.
Definition IRCore.cpp:898
Represents a Python MlirNamedAttr, carrying an optional owned name.
Definition IRCore.h:1054
PyNamedAttribute(MlirAttribute attr, std::string ownedName)
Constructs a PyNamedAttr that retains an owned name.
Definition IRCore.cpp:1907
Template for a reference to a concrete type which captures a python reference to its underlying pytho...
Definition IRCore.h:66
nanobind::object releaseObject()
Releases the object held by this instance, returning it.
Definition IRCore.h:104
void dunderSetItem(const std::string &name, const PyAttribute &attr)
Definition IRCore.cpp:2426
nanobind::typed< nanobind::object, PyAttribute > dunderGetItemNamed(const std::string &name)
Definition IRCore.cpp:2393
nanobind::typed< nanobind::object, std::optional< PyAttribute > > get(const std::string &key, nanobind::object defaultValue)
Definition IRCore.cpp:2403
static void forEachAttr(MlirOperation op, std::function< void(MlirStringRef, MlirAttribute)> fn)
Definition IRCore.cpp:2448
PyNamedAttribute dunderGetItemIndexed(intptr_t index)
Definition IRCore.cpp:2411
nanobind::typed< nanobind::object, PyOpOperand > dunderNext()
Definition IRCore.cpp:382
PyOpOperandList(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2238
void dunderSetItem(intptr_t index, PyValue value)
Definition IRCore.cpp:2246
nanobind::typed< nanobind::object, PyOpView > getOwner() const
Definition IRCore.cpp:363
PyOpOperands(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2281
Sliceable< PyOpOperandList, PyOpOperand > SliceableT
Definition IRCore.cpp:2279
PyOpResultList(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:1400
PyOpSuccessors(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2310
void dunderSetItem(intptr_t index, PyBlock block)
Definition IRCore.cpp:2318
A PyOpView is equivalent to the C++ "Op" wrappers: these are the basis for providing more instance-sp...
Definition IRCore.h:761
PyOpView(const nanobind::object &operationObject)
Definition IRCore.cpp:1740
static nanobind::typed< nanobind::object, PyOperation > buildGeneric(std::string_view name, std::tuple< int, bool > opRegionSpec, nanobind::object operandSegmentSpecObj, nanobind::object resultSegmentSpecObj, std::optional< nanobind::sequence > resultTypeList, nanobind::sequence operandList, std::optional< nanobind::dict > attributes, std::optional< std::vector< PyBlock * > > successors, std::optional< int > regions, PyLocation &location, const nanobind::object &maybeIp)
Definition IRCore.cpp:1563
static nanobind::object constructDerived(const nanobind::object &cls, const nanobind::object &operation)
Construct an instance of a class deriving from OpView, bypassing its __init__ method.
Definition IRCore.cpp:1732
Base class for PyOperation and PyOpView which exposes the primary, user visible methods for manipulat...
Definition IRCore.h:604
bool isBeforeInBlock(PyOperationBase &other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Definition IRCore.cpp:1164
nanobind::object getAsm(bool binary, std::optional< int64_t > largeElementsLimit, std::optional< int64_t > largeResourceLimit, bool enableDebugInfo, bool prettyDebugInfo, bool printGenericOpForm, bool useLocalScope, bool useNameLocAsPrefix, bool assumeVerified, bool skipRegions)
Definition IRCore.cpp:1118
void print(std::optional< int64_t > largeElementsLimit, std::optional< int64_t > largeResourceLimit, bool enableDebugInfo, bool prettyDebugInfo, bool printGenericOpForm, bool useLocalScope, bool useNameLocAsPrefix, bool assumeVerified, nanobind::object fileObject, bool binary, bool skipRegions)
Implements the bound 'print' method and helps with others.
Definition IRCore.cpp:1017
void writeBytecode(const nanobind::object &fileObject, std::optional< int64_t > bytecodeVersion)
Definition IRCore.cpp:1065
virtual PyOperation & getOperation()=0
Each must provide access to the raw Operation.
void moveAfter(PyOperationBase &other)
Moves the operation before or after the other operation.
Definition IRCore.cpp:1146
void walk(std::function< PyWalkResult(MlirOperation)> callback, PyWalkOrder walkOrder)
Definition IRCore.cpp:1086
nanobind::typed< nanobind::object, PyOpView > dunderNext()
Definition IRCore.cpp:294
Operations are exposed by the C-API as a forward-only linked list.
Definition IRCore.h:1604
nanobind::typed< nanobind::object, PyOpView > dunderGetItem(intptr_t index)
Definition IRCore.cpp:333
static nanobind::object create(std::string_view name, std::optional< std::vector< PyType * > > results, const MlirValue *operands, size_t numOperands, std::optional< nanobind::dict > attributes, std::optional< std::vector< PyBlock * > > successors, int regions, PyLocation &location, const nanobind::object &ip, bool inferType)
Creates an operation. See corresponding python docstring.
Definition IRCore.cpp:1228
void setInvalid()
Invalidate the operation.
Definition IRCore.h:728
PyOperation & getOperation() override
Each must provide access to the raw Operation.
Definition IRCore.h:661
static PyOperationRef parse(PyMlirContextRef contextRef, const std::string &sourceStr, const std::string &sourceName)
Parses a source string (either text assembly or bytecode), creating a detached operation.
Definition IRCore.cpp:974
nanobind::object clone(const nanobind::object &ip)
Clones this operation.
Definition IRCore.cpp:1343
static nanobind::object createFromCapsule(const nanobind::object &capsule)
Creates a PyOperation from the MlirOperation wrapped by a capsule.
Definition IRCore.cpp:1204
std::optional< PyOperationRef > getParentOperation()
Gets the parent operation or raises an exception if the operation has no parent.
Definition IRCore.cpp:1180
nanobind::object createOpView()
Creates an OpView suitable for this operation.
Definition IRCore.cpp:1352
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirOperation.
Definition IRCore.cpp:1199
static PyOperationRef forOperation(PyMlirContextRef contextRef, MlirOperation operation, nanobind::object parentKeepAlive=nanobind::object())
Returns a PyOperation for the given MlirOperation, optionally associating it with a parentKeepAlive.
Definition IRCore.cpp:958
void detachFromParent()
Detaches the operation from its parent block and updates its state accordingly.
Definition IRCore.cpp:986
void erase()
Erases the underlying MlirOperation, removes its pointer from the parent context's live operations ma...
Definition IRCore.cpp:1363
PyBlock getBlock()
Gets the owning block or raises an exception if the operation has no owning block.
Definition IRCore.cpp:1190
static PyOperationRef createDetached(PyMlirContextRef contextRef, MlirOperation operation, nanobind::object parentKeepAlive=nanobind::object())
Creates a detached operation.
Definition IRCore.cpp:965
PyOperation(PyMlirContextRef contextRef, MlirOperation operation)
Definition IRCore.cpp:913
void setAttached(const nanobind::object &parent=nanobind::object())
Definition IRCore.cpp:1001
Regions of an op are fixed length and indexed numerically so are represented with a sequence-like con...
Definition IRCore.h:1524
PyRegionList(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:194
PyStringAttribute insert(PyOperationBase &symbol)
Inserts the given operation into the symbol table.
Definition IRCore.cpp:2059
PySymbolTable(PyOperationBase &operation)
Constructs a symbol table for the given operation.
Definition IRCore.cpp:2023
static PyStringAttribute getVisibility(PyOperationBase &symbol)
Gets and sets the visibility of a symbol op.
Definition IRCore.cpp:2099
void erase(PyOperationBase &symbol)
Removes the given operation from the symbol table and erases it.
Definition IRCore.cpp:2044
static void walkSymbolTables(PyOperationBase &from, bool allSymUsesVisible, nanobind::object callback)
Walks all symbol tables under and including 'from'.
Definition IRCore.cpp:2140
nanobind::object dunderGetItem(const std::string &name)
Returns the symbol (opview) with the given name, throws if there is no such symbol in the table.
Definition IRCore.cpp:2031
static void replaceAllSymbolUses(const std::string &oldSymbol, const std::string &newSymbol, PyOperationBase &from)
Replaces all symbol uses within an operation.
Definition IRCore.cpp:2128
static PyStringAttribute getSymbolName(PyOperationBase &symbol)
Gets and sets the name of a symbol op.
Definition IRCore.cpp:2071
void dunderDel(const std::string &name)
Removes the operation with the given name from the symbol table and erases it, throws if there is no ...
Definition IRCore.cpp:2054
static void setSymbolName(PyOperationBase &symbol, const std::string &name)
Definition IRCore.cpp:2084
static void setVisibility(PyOperationBase &symbol, const std::string &visibility)
Definition IRCore.cpp:2110
Tracks an entry in the thread context stack.
Definition IRCore.h:137
static PyInsertionPoint * getDefaultInsertionPoint()
Gets the top of stack insertion point and return nullptr if not defined.
Definition IRCore.cpp:638
static nanobind::object pushInsertionPoint(nanobind::object insertionPoint)
Definition IRCore.cpp:666
static void popInsertionPoint(PyInsertionPoint &insertionPoint)
Definition IRCore.cpp:678
static PyLocation * getDefaultLocation()
Gets the top of stack location and returns nullptr if not defined.
Definition IRCore.cpp:643
static PyThreadContextEntry * getTopOfStack()
Stack management.
Definition IRCore.cpp:586
static nanobind::object pushLocation(nanobind::object location)
Definition IRCore.cpp:689
static nanobind::object pushContext(nanobind::object context)
Definition IRCore.cpp:648
static PyMlirContext * getDefaultContext()
Gets the top of stack context and return nullptr if not defined.
Definition IRCore.cpp:633
static std::vector< PyThreadContextEntry > & getStack()
Gets the thread local stack.
Definition IRCore.cpp:581
Wrapper around MlirLlvmThreadPool Python object owns the C++ thread pool.
Definition IRCore.h:193
A TypeID provides an efficient and unique identifier for a specific C++ type.
Definition IRCore.h:927
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirTypeID.
Definition IRCore.cpp:1953
bool operator==(const PyTypeID &other) const
Definition IRCore.cpp:1963
static PyTypeID createFromCapsule(nanobind::object capsule)
Creates a PyTypeID from the MlirTypeID wrapped by a capsule.
Definition IRCore.cpp:1957
Wrapper around the generic MlirType.
Definition IRCore.h:901
PyType(PyMlirContextRef contextRef, MlirType type)
Definition IRCore.h:903
bool operator==(const PyType &other) const
Definition IRCore.cpp:1919
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirType.
Definition IRCore.cpp:1923
static PyType createFromCapsule(nanobind::object capsule)
Creates a PyType from the MlirType wrapped by a capsule.
Definition IRCore.cpp:1927
nanobind::typed< nanobind::object, PyType > maybeDownCast()
Definition IRCore.cpp:1935
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirValue.
Definition IRCore.cpp:1971
PyValue(PyOperationRef parentOperation, MlirValue value)
Definition IRCore.h:1320
nanobind::typed< nanobind::object, std::variant< PyBlockArgument, PyOpResult, PyValue > > maybeDownCast()
Definition IRCore.cpp:1990
static PyValue createFromCapsule(nanobind::object capsule)
Creates a PyValue from the MlirValue wrapped by a capsule.
Definition IRCore.cpp:2011
MLIR_CAPI_EXPORTED intptr_t mlirDiagnosticGetNumNotes(MlirDiagnostic diagnostic)
Returns the number of notes attached to the diagnostic.
MLIR_CAPI_EXPORTED MlirDiagnosticSeverity mlirDiagnosticGetSeverity(MlirDiagnostic diagnostic)
Returns the severity of the diagnostic.
MLIR_CAPI_EXPORTED void mlirDiagnosticPrint(MlirDiagnostic diagnostic, MlirStringCallback callback, void *userData)
Prints a diagnostic using the provided callback.
MLIR_CAPI_EXPORTED MlirDiagnostic mlirDiagnosticGetNote(MlirDiagnostic diagnostic, intptr_t pos)
Returns pos-th note attached to the diagnostic.
MLIR_CAPI_EXPORTED void mlirEmitError(MlirLocation location, const char *message)
Emits an error at the given location through the diagnostics engine.
MLIR_CAPI_EXPORTED MlirDiagnosticHandlerID mlirContextAttachDiagnosticHandler(MlirContext context, MlirDiagnosticHandler handler, void *userData, void(*deleteUserData)(void *))
Attaches the diagnostic handler to the context.
struct MlirDiagnostic MlirDiagnostic
Definition Diagnostics.h:29
MLIR_CAPI_EXPORTED void mlirContextDetachDiagnosticHandler(MlirContext context, MlirDiagnosticHandlerID id)
Detaches an attached diagnostic handler from the context given its identifier.
uint64_t MlirDiagnosticHandlerID
Opaque identifier of a diagnostic handler, useful to detach a handler.
Definition Diagnostics.h:41
MLIR_CAPI_EXPORTED MlirLocation mlirDiagnosticGetLocation(MlirDiagnostic diagnostic)
Returns the location at which the diagnostic is reported.
MLIR_CAPI_EXPORTED MlirAttribute mlirDenseI32ArrayGet(MlirContext ctx, intptr_t size, int32_t const *values)
MLIR_CAPI_EXPORTED MlirAttribute mlirStringAttrGet(MlirContext ctx, MlirStringRef str)
Creates a string attribute in the given context containing the given string.
MLIR_CAPI_EXPORTED MlirDynamicOpTrait mlirDynamicOpTraitIsTerminatorCreate(void)
Get the dynamic op trait that indicates the operation is a terminator.
MLIR_CAPI_EXPORTED MlirDynamicOpTrait mlirDynamicOpTraitIsIsolatedFromAboveCreate(void)
Get the dynamic op trait that indicates regions are isolated from above.
MLIR_CAPI_EXPORTED MlirTypeID mlirDynamicOpTraitRecursiveMemoryEffectsGetTypeID(void)
Get the type ID of the dynamic op trait that indicates memory effects of an operation includes the ef...
MLIR_CAPI_EXPORTED MlirTypeID mlirDynamicOpTraitIsIsolatedFromAboveGetTypeID(void)
Get the type ID of the dynamic op trait that indicates regions are isolated from above.
MLIR_CAPI_EXPORTED MlirTypeID mlirDynamicOpTraitIsTerminatorGetTypeID(void)
Get the type ID of the dynamic op trait that indicates the operation is a terminator.
MLIR_CAPI_EXPORTED MlirDynamicOpTrait mlirDynamicOpTraitRecursiveMemoryEffectsCreate(void)
Get the dynamic op trait that indicates memory effects of an operation includes the effects of operat...
MLIR_CAPI_EXPORTED MlirDynamicOpTrait mlirDynamicOpTraitCreate(MlirTypeID typeID, MlirDynamicOpTraitCallbacks callbacks, void *userData)
Create a custom dynamic op trait with the given type ID and callbacks.
MLIR_CAPI_EXPORTED bool mlirDynamicOpTraitAttach(MlirDynamicOpTrait dynamicOpTrait, MlirStringRef opName, MlirContext context)
Attach a dynamic op trait to the given operation name.
MLIR_CAPI_EXPORTED MlirTypeID mlirDynamicOpTraitNoTerminatorGetTypeID(void)
Get the type ID of the dynamic op trait that indicates regions have no terminator.
MLIR_CAPI_EXPORTED MlirDynamicOpTrait mlirDynamicOpTraitNoTerminatorCreate(void)
Get the dynamic op trait that indicates regions have no terminator.
MLIR_CAPI_EXPORTED MlirAttribute mlirLocationGetAttribute(MlirLocation location)
Returns the underlying location attribute of this location.
Definition IR.cpp:282
MlirWalkResult(* MlirOperationWalkCallback)(MlirOperation, void *userData)
Operation walker type.
Definition IR.h:925
MLIR_CAPI_EXPORTED MlirLocation mlirValueGetLocation(MlirValue v)
Gets the location of the value.
Definition IR.cpp:1267
MLIR_CAPI_EXPORTED unsigned mlirContextGetNumThreads(MlirContext context)
Gets the number of threads of the thread pool of the context when multithreading is enabled.
Definition IR.cpp:122
MLIR_CAPI_EXPORTED void mlirOperationWriteBytecode(MlirOperation op, MlirStringCallback callback, void *userData)
Same as mlirOperationPrint but writing the bytecode format.
Definition IR.cpp:896
MLIR_CAPI_EXPORTED MlirLocation mlirLocationFileLineColGet(MlirContext context, MlirStringRef filename, unsigned line, unsigned col)
Creates an File/Line/Column location owned by the given context.
Definition IR.cpp:290
MLIR_CAPI_EXPORTED void mlirSymbolTableWalkSymbolTables(MlirOperation from, bool allSymUsesVisible, void(*callback)(MlirOperation, bool, void *userData), void *userData)
Walks all symbol table operations nested within, and including, op.
Definition IR.cpp:1449
MLIR_CAPI_EXPORTED MlirStringRef mlirDialectGetNamespace(MlirDialect dialect)
Returns the namespace of the given dialect.
Definition IR.cpp:154
MLIR_CAPI_EXPORTED int mlirLocationFileLineColRangeGetEndColumn(MlirLocation location)
Getter for end_column of FileLineColRange.
Definition IR.cpp:328
MLIR_CAPI_EXPORTED MlirAttribute mlirSymbolTableInsert(MlirSymbolTable symbolTable, MlirOperation operation)
Inserts the given operation into the given symbol table.
Definition IR.cpp:1428
MlirWalkOrder
Traversal order for operation walk.
Definition IR.h:918
MLIR_CAPI_EXPORTED MlirNamedAttribute mlirNamedAttributeGet(MlirIdentifier name, MlirAttribute attr)
Associates an attribute with the name. Takes ownership of neither.
Definition IR.cpp:1376
MLIR_CAPI_EXPORTED MlirLocation mlirLocationNameGetChildLoc(MlirLocation location)
Getter for childLoc of Name.
Definition IR.cpp:409
MLIR_CAPI_EXPORTED void mlirSymbolTableErase(MlirSymbolTable symbolTable, MlirOperation operation)
Removes the given operation from the symbol table and erases it.
Definition IR.cpp:1433
MLIR_CAPI_EXPORTED void mlirContextAppendDialectRegistry(MlirContext ctx, MlirDialectRegistry registry)
Append the contents of the given dialect registry to the registry associated with the context.
Definition IR.cpp:84
MLIR_CAPI_EXPORTED MlirStringRef mlirIdentifierStr(MlirIdentifier ident)
Gets the string value of the identifier.
Definition IR.cpp:1397
MLIR_CAPI_EXPORTED MlirType mlirTypeParseGet(MlirContext context, MlirStringRef type)
Parses a type. The type is owned by the context.
Definition IR.cpp:1310
MLIR_CAPI_EXPORTED MlirOpOperand mlirOpOperandGetNextUse(MlirOpOperand opOperand)
Returns an op operand representing the next use of the value, or a null op operand if there is no nex...
Definition IR.cpp:1293
MLIR_CAPI_EXPORTED void mlirContextSetAllowUnregisteredDialects(MlirContext context, bool allow)
Sets whether unregistered dialects are allowed in this context.
Definition IR.cpp:73
MLIR_CAPI_EXPORTED void mlirRegionInsertOwnedBlockBefore(MlirRegion region, MlirBlock reference, MlirBlock block)
Takes a block owned by the caller and inserts it before the (non-owned) reference block in the given ...
Definition IR.cpp:1006
MLIR_CAPI_EXPORTED void mlirContextBeginTransientScope(MlirContext context)
Begins a transient scope on the context, freezing the base layer (loaded dialects,...
Definition IR.cpp:130
MLIR_CAPI_EXPORTED unsigned mlirLocationFusedGetNumLocations(MlirLocation location)
Getter for number of locations fused together.
Definition IR.cpp:372
MLIR_CAPI_EXPORTED void mlirValueReplaceAllUsesOfWith(MlirValue of, MlirValue with)
Replace all uses of 'of' value with the 'with' value, updating anything in the IR that uses 'of' to u...
Definition IR.cpp:1239
MLIR_CAPI_EXPORTED void mlirValuePrintAsOperand(MlirValue value, MlirAsmState state, MlirStringCallback callback, void *userData)
Prints a value as an operand (i.e., the ValueID).
Definition IR.cpp:1222
MLIR_CAPI_EXPORTED MlirLocation mlirLocationUnknownGet(MlirContext context)
Creates a location with unknown position owned by the given context.
Definition IR.cpp:420
MLIR_CAPI_EXPORTED MlirOperation mlirOpOperandGetOwner(MlirOpOperand opOperand)
Returns the owner operation of an op operand.
Definition IR.cpp:1281
MLIR_CAPI_EXPORTED MlirIdentifier mlirLocationFileLineColRangeGetFilename(MlirLocation location)
Getter for filename of FileLineColRange.
Definition IR.cpp:306
MLIR_CAPI_EXPORTED void mlirLocationFusedGetLocations(MlirLocation location, MlirLocation *locationsCPtr)
Getter for locations of Fused.
Definition IR.cpp:378
MLIR_CAPI_EXPORTED void mlirAttributePrint(MlirAttribute attr, MlirStringCallback callback, void *userData)
Prints a location by sending chunks of the string representation and forwarding userData to callback`...
Definition IR.cpp:1368
MLIR_CAPI_EXPORTED MlirRegion mlirBlockGetParentRegion(MlirBlock block)
Returns the region that contains this block.
Definition IR.cpp:1045
MLIR_CAPI_EXPORTED void mlirOperationMoveBefore(MlirOperation op, MlirOperation other)
Moves the given operation immediately before the other operation in its parent block.
Definition IR.cpp:920
MLIR_CAPI_EXPORTED void mlirValueReplaceAllUsesExcept(MlirValue of, MlirValue with, intptr_t numExceptions, MlirOperation *exceptions)
Replace all uses of 'of' value with 'with' value, updating anything in the IR that uses 'of' to use '...
Definition IR.cpp:1243
MLIR_CAPI_EXPORTED void mlirOperationPrintWithState(MlirOperation op, MlirAsmState state, MlirStringCallback callback, void *userData)
Same as mlirOperationPrint but accepts AsmState controlling the printing behavior as well as caching ...
Definition IR.cpp:887
MlirWalkResult
Operation walk result.
Definition IR.h:911
MLIR_CAPI_EXPORTED void mlirRegionInsertOwnedBlock(MlirRegion region, intptr_t pos, MlirBlock block)
Takes a block owned by the caller and inserts it at pos to the given region.
Definition IR.cpp:986
static bool mlirTypeIsNull(MlirType type)
Checks whether a type is null.
Definition IR.h:1245
MLIR_CAPI_EXPORTED bool mlirContextIsRegisteredOperation(MlirContext context, MlirStringRef name)
Returns whether the given fully-qualified operation (i.e.
Definition IR.cpp:105
MLIR_CAPI_EXPORTED intptr_t mlirBlockGetNumArguments(MlirBlock block)
Returns the number of arguments of the block.
Definition IR.cpp:1114
MLIR_CAPI_EXPORTED int mlirLocationFileLineColRangeGetStartLine(MlirLocation location)
Getter for start_line of FileLineColRange.
Definition IR.cpp:310
MLIR_CAPI_EXPORTED MlirLocation mlirLocationFusedGet(MlirContext ctx, intptr_t nLocations, MlirLocation const *locations, MlirAttribute metadata)
Creates a fused location with an array of locations and metadata.
Definition IR.cpp:364
MLIR_CAPI_EXPORTED MlirDialect mlirContextGetLoadedDialect(MlirContext context, MlirStringRef name)
Gets the dialect instance owned by the given context using the dialect namespace to identify it.
Definition IR.cpp:100
MLIR_CAPI_EXPORTED void mlirBlockInsertOwnedOperationBefore(MlirBlock block, MlirOperation reference, MlirOperation operation)
Takes an operation owned by the caller and inserts it before the (non-owned) reference operation in t...
Definition IR.cpp:1095
static bool mlirContextIsNull(MlirContext context)
Checks whether a context is null.
Definition IR.h:105
MLIR_CAPI_EXPORTED MlirDialect mlirContextGetOrLoadDialect(MlirContext context, MlirStringRef name)
Gets the dialect instance owned by the given context using the dialect namespace to identify it,...
Definition IR.cpp:95
MLIR_CAPI_EXPORTED void mlirRegionInsertOwnedBlockAfter(MlirRegion region, MlirBlock reference, MlirBlock block)
Takes a block owned by the caller and inserts it after the (non-owned) reference block in the given r...
Definition IR.cpp:992
MLIR_CAPI_EXPORTED MlirBlock mlirBlockCreate(intptr_t nArgs, MlirType const *args, MlirLocation const *locs)
Creates a new empty block with the given argument types and transfers ownership to the caller.
Definition IR.cpp:1029
static bool mlirBlockIsNull(MlirBlock block)
Checks whether a block is null.
Definition IR.h:1011
MLIR_CAPI_EXPORTED void mlirBlockAppendOwnedOperation(MlirBlock block, MlirOperation operation)
Takes an operation owned by the caller and appends it to the block.
Definition IR.cpp:1070
MLIR_CAPI_EXPORTED MlirValue mlirBlockGetArgument(MlirBlock block, intptr_t pos)
Returns pos-th argument of the block.
Definition IR.cpp:1132
MLIR_CAPI_EXPORTED MlirOperation mlirSymbolTableLookup(MlirSymbolTable symbolTable, MlirStringRef name)
Looks up a symbol with the given name in the given symbol table and returns the operation that corres...
Definition IR.cpp:1423
MLIR_CAPI_EXPORTED MlirContext mlirTypeGetContext(MlirType type)
Gets the context that a type was created with.
Definition IR.cpp:1314
MLIR_CAPI_EXPORTED MlirLocation mlirLocationFileLineColRangeGet(MlirContext context, MlirStringRef filename, unsigned start_line, unsigned start_col, unsigned end_line, unsigned end_col)
Creates an File/Line/Column range location owned by the given context.
Definition IR.cpp:298
MLIR_CAPI_EXPORTED bool mlirOpOperandIsNull(MlirOpOperand opOperand)
Returns whether the op operand is null.
Definition IR.cpp:1279
MLIR_CAPI_EXPORTED MlirSymbolTable mlirSymbolTableCreate(MlirOperation operation)
Creates a symbol table for the given operation.
Definition IR.cpp:1413
MLIR_CAPI_EXPORTED bool mlirLocationEqual(MlirLocation l1, MlirLocation l2)
Checks if two locations are equal.
Definition IR.cpp:432
MLIR_CAPI_EXPORTED int mlirLocationFileLineColRangeGetStartColumn(MlirLocation location)
Getter for start_column of FileLineColRange.
Definition IR.cpp:316
MLIR_CAPI_EXPORTED bool mlirContextIsInTransientScope(MlirContext context)
Returns whether the context is currently in a transient scope.
Definition IR.cpp:138
MLIR_CAPI_EXPORTED bool mlirLocationIsAFused(MlirLocation location)
Checks whether the given location is an Fused.
Definition IR.cpp:392
static bool mlirLocationIsNull(MlirLocation location)
Checks if the location is null.
Definition IR.h:398
MLIR_CAPI_EXPORTED MlirValue mlirBlockAddArgument(MlirBlock block, MlirType type, MlirLocation loc)
Appends an argument of the specified type to the block.
Definition IR.cpp:1118
MLIR_CAPI_EXPORTED void mlirOperationPrintWithFlags(MlirOperation op, MlirOpPrintingFlags flags, MlirStringCallback callback, void *userData)
Same as mlirOperationPrint but accepts flags controlling the printing behavior.
Definition IR.cpp:881
MLIR_CAPI_EXPORTED MlirOpOperand mlirValueGetFirstUse(MlirValue value)
Returns an op operand representing the first use of the value, or a null op operand if there are no u...
Definition IR.cpp:1229
MLIR_CAPI_EXPORTED void mlirContextEndTransientScope(MlirContext context)
Ends the transient scope and resets the context to the base state, pruning transient types,...
Definition IR.cpp:134
MLIR_CAPI_EXPORTED void mlirContextSetThreadPool(MlirContext context, MlirLlvmThreadPool threadPool)
Sets the thread pool of the context explicitly, enabling multithreading in the process.
Definition IR.cpp:117
MLIR_CAPI_EXPORTED bool mlirOperationVerify(MlirOperation op)
Verify the operation and return true if it passes, false if it fails.
Definition IR.cpp:912
MLIR_CAPI_EXPORTED bool mlirTypeEqual(MlirType t1, MlirType t2)
Checks if two types are equal.
Definition IR.cpp:1326
MLIR_CAPI_EXPORTED unsigned mlirOpOperandGetOperandNumber(MlirOpOperand opOperand)
Returns the operand number of an op operand.
Definition IR.cpp:1289
MLIR_CAPI_EXPORTED MlirLocation mlirLocationCallSiteGetCaller(MlirLocation location)
Getter for caller of CallSite.
Definition IR.cpp:351
MLIR_CAPI_EXPORTED MlirOperation mlirBlockGetTerminator(MlirBlock block)
Returns the terminator operation in the block or null if no terminator.
Definition IR.cpp:1060
MLIR_CAPI_EXPORTED MlirIdentifier mlirLocationNameGetName(MlirLocation location)
Getter for name of Name.
Definition IR.cpp:405
MLIR_CAPI_EXPORTED bool mlirOperationIsBeforeInBlock(MlirOperation op, MlirOperation other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Definition IR.cpp:924
MLIR_CAPI_EXPORTED MlirLocation mlirLocationFromAttribute(MlirAttribute attribute)
Creates a location from a location attribute.
Definition IR.cpp:286
MLIR_CAPI_EXPORTED MlirTypeID mlirTypeGetTypeID(MlirType type)
Gets the type ID of the type.
Definition IR.cpp:1318
MLIR_CAPI_EXPORTED MlirStringRef mlirSymbolTableGetVisibilityAttributeName(void)
Returns the name of the attribute used to store symbol visibility.
Definition IR.cpp:1409
static bool mlirDialectIsNull(MlirDialect dialect)
Checks if the dialect is null.
Definition IR.h:204
MLIR_CAPI_EXPORTED MlirAttribute mlirLocationFusedGetMetadata(MlirLocation location)
Getter for metadata of Fused.
Definition IR.cpp:386
MLIR_CAPI_EXPORTED MlirBlock mlirBlockGetNextInRegion(MlirBlock block)
Returns the block immediately following the given block in its parent region.
Definition IR.cpp:1049
MLIR_CAPI_EXPORTED MlirLocation mlirLocationCallSiteGet(MlirLocation callee, MlirLocation caller)
Creates a call site location with a callee and a caller.
Definition IR.cpp:342
MLIR_CAPI_EXPORTED bool mlirLocationIsAName(MlirLocation location)
Checks whether the given location is an Name.
Definition IR.cpp:416
static bool mlirDialectRegistryIsNull(MlirDialectRegistry registry)
Checks if the dialect registry is null.
Definition IR.h:266
MLIR_CAPI_EXPORTED void mlirOperationWalk(MlirOperation op, MlirOperationWalkCallback callback, void *userData, MlirWalkOrder walkOrder)
Walks operation op in walkOrder and calls callback on that operation.
Definition IR.cpp:942
MLIR_CAPI_EXPORTED MlirContext mlirContextCreateWithThreading(bool threadingEnabled)
Creates an MLIR context with an explicit setting of the multithreading setting and transfers its owne...
Definition IR.cpp:55
MLIR_CAPI_EXPORTED MlirOperation mlirBlockGetParentOperation(MlirBlock)
Returns the closest surrounding operation that contains this block.
Definition IR.cpp:1041
MLIR_CAPI_EXPORTED MlirContext mlirLocationGetContext(MlirLocation location)
Gets the context that a location was created with.
Definition IR.cpp:436
MLIR_CAPI_EXPORTED void mlirBlockEraseArgument(MlirBlock block, unsigned index)
Erase the argument at 'index' and remove it from the argument list.
Definition IR.cpp:1123
MLIR_CAPI_EXPORTED void mlirAttributeDump(MlirAttribute attr)
Prints the attribute to the standard error stream.
Definition IR.cpp:1374
MLIR_CAPI_EXPORTED MlirLogicalResult mlirSymbolTableReplaceAllSymbolUses(MlirStringRef oldSymbol, MlirStringRef newSymbol, MlirOperation from)
Attempt to replace all uses that are nested within the given operation of the given symbol 'oldSymbol...
Definition IR.cpp:1438
MLIR_CAPI_EXPORTED void mlirRegionAppendOwnedBlock(MlirRegion region, MlirBlock block)
Takes a block owned by the caller and appends it to the given region.
Definition IR.cpp:982
MLIR_CAPI_EXPORTED MlirOperation mlirBlockGetFirstOperation(MlirBlock block)
Returns the first operation in the block.
Definition IR.cpp:1053
static bool mlirRegionIsNull(MlirRegion region)
Checks whether a region is null.
Definition IR.h:950
MLIR_CAPI_EXPORTED MlirDialect mlirTypeGetDialect(MlirType type)
Gets the dialect a type belongs to.
Definition IR.cpp:1322
MLIR_CAPI_EXPORTED MlirIdentifier mlirIdentifierGet(MlirContext context, MlirStringRef str)
Gets an identifier with the given string value.
Definition IR.cpp:1385
MLIR_CAPI_EXPORTED void mlirContextLoadAllAvailableDialects(MlirContext context)
Eagerly loads all available dialects registered with a context, making them available for use for IR ...
Definition IR.cpp:113
MLIR_CAPI_EXPORTED MlirLlvmThreadPool mlirContextGetThreadPool(MlirContext context)
Gets the thread pool of the context when enabled multithreading, otherwise an assertion is raised.
Definition IR.cpp:126
MLIR_CAPI_EXPORTED int mlirLocationFileLineColRangeGetEndLine(MlirLocation location)
Getter for end_line of FileLineColRange.
Definition IR.cpp:322
MLIR_CAPI_EXPORTED MlirLocation mlirLocationNameGet(MlirContext context, MlirStringRef name, MlirLocation childLoc)
Creates a name location owned by the given context.
Definition IR.cpp:396
MLIR_CAPI_EXPORTED void mlirContextEnableMultithreading(MlirContext context, bool enable)
Set threading mode (must be set to false to mlir-print-ir-after-all).
Definition IR.cpp:109
MLIR_CAPI_EXPORTED MlirLocation mlirLocationCallSiteGetCallee(MlirLocation location)
Getter for callee of CallSite.
Definition IR.cpp:346
MLIR_CAPI_EXPORTED MlirContext mlirValueGetContext(MlirValue v)
Gets the context that a value was created with.
Definition IR.cpp:1271
MLIR_CAPI_EXPORTED MlirStringRef mlirSymbolTableGetSymbolAttributeName(void)
Returns the name of the attribute used to store symbol names compatible with symbol tables.
Definition IR.cpp:1405
MLIR_CAPI_EXPORTED MlirRegion mlirRegionCreate(void)
Creates a new empty region and transfers ownership to the caller.
Definition IR.cpp:969
MLIR_CAPI_EXPORTED void mlirBlockDetach(MlirBlock block)
Detach a block from the owning region and assume ownership.
Definition IR.cpp:1109
MLIR_CAPI_EXPORTED void mlirOperationDump(MlirOperation op)
Prints an operation to stderr.
Definition IR.cpp:910
static bool mlirSymbolTableIsNull(MlirSymbolTable symbolTable)
Returns true if the symbol table is null.
Definition IR.h:1335
MLIR_CAPI_EXPORTED bool mlirContextGetAllowUnregisteredDialects(MlirContext context)
Returns whether the context allows unregistered dialects.
Definition IR.cpp:77
MLIR_CAPI_EXPORTED void mlirOperationReplaceUsesOfWith(MlirOperation op, MlirValue of, MlirValue with)
Replace uses of 'of' value with the 'with' value inside the 'op' operation.
Definition IR.cpp:960
MLIR_CAPI_EXPORTED void mlirOperationMoveAfter(MlirOperation op, MlirOperation other)
Moves the given operation immediately after the other operation in its parent block.
Definition IR.cpp:916
MLIR_CAPI_EXPORTED void mlirValuePrint(MlirValue value, MlirStringCallback callback, void *userData)
Prints a block by sending chunks of the string representation and forwarding userData to callback`.
Definition IR.cpp:1216
MLIR_CAPI_EXPORTED MlirLogicalResult mlirOperationWriteBytecodeWithConfig(MlirOperation op, MlirBytecodeWriterConfig config, MlirStringCallback callback, void *userData)
Same as mlirOperationWriteBytecode but with writer config and returns failure only if desired bytecod...
Definition IR.cpp:903
MLIR_CAPI_EXPORTED void mlirContextDestroy(MlirContext context)
Takes an MLIR context owned by the caller and destroys it.
Definition IR.cpp:71
MLIR_CAPI_EXPORTED MlirBlock mlirRegionGetFirstBlock(MlirRegion region)
Gets the first block in the region.
Definition IR.cpp:975
static MlirStringRef mlirStringRefCreate(const char *str, size_t length)
Constructs a string reference from the pointer and length.
Definition Support.h:87
static MlirLogicalResult mlirLogicalResultFailure(void)
Creates a logical result representing a failure.
Definition Support.h:143
struct MlirLogicalResult MlirLogicalResult
Definition Support.h:124
MLIR_CAPI_EXPORTED int mlirLlvmThreadPoolGetMaxConcurrency(MlirLlvmThreadPool pool)
Returns the maximum number of threads in the thread pool.
Definition Support.cpp:38
MLIR_CAPI_EXPORTED void mlirLlvmThreadPoolDestroy(MlirLlvmThreadPool pool)
Destroy an LLVM thread pool.
Definition Support.cpp:34
MLIR_CAPI_EXPORTED MlirLlvmThreadPool mlirLlvmThreadPoolCreate(void)
Create an LLVM thread pool.
Definition Support.cpp:30
MLIR_CAPI_EXPORTED size_t mlirTypeIDHashValue(MlirTypeID typeID)
Returns the hash value of the type id.
Definition Support.cpp:93
static MlirLogicalResult mlirLogicalResultSuccess(void)
Creates a logical result representing a success.
Definition Support.h:137
struct MlirStringRef MlirStringRef
Definition Support.h:82
static bool mlirLogicalResultIsFailure(MlirLogicalResult res)
Checks if the given logical result represents a failure.
Definition Support.h:132
static bool mlirTypeIDIsNull(MlirTypeID typeID)
Checks whether a type id is null.
Definition Support.h:201
MLIR_CAPI_EXPORTED bool mlirTypeIDEqual(MlirTypeID typeID1, MlirTypeID typeID2)
Checks if two type ids are equal.
Definition Support.cpp:89
void walk(Operation *op, function_ref< void(Region *)> callback, WalkOrder order)
Walk all of the regions, blocks, or operations nested under (and including) the given operation.
Definition Visitors.h:102
MLIR_PYTHON_API_EXPORTED MlirValue getUniqueResult(MlirOperation operation)
Definition IRCore.cpp:1529
static std::string formatMLIRError(const MLIRError &e)
Definition IRCore.cpp:2887
PyOperationEquivalenceFlags
Flags controlling structural operation equivalence and hashing.
Definition IRCore.h:370
MLIR_PYTHON_API_EXPORTED void populateRoot(nanobind::module_ &m)
static void maybeInsertOperation(PyOperationRef &op, const nb::object &maybeIp)
Definition IRCore.cpp:1213
static void populateResultTypes(std::string_view name, nb::sequence resultTypeList, const nb::object &resultSegmentSpecObj, std::vector< int32_t > &resultSegmentLengths, std::vector< PyType * > &resultTypes)
Definition IRCore.cpp:1442
PyObjectRef< PyMlirContext > PyMlirContextRef
Wrapper around MlirContext.
Definition IRCore.h:210
static MlirValue getOpResultOrValue(nb::handle operand)
Definition IRCore.cpp:1544
PyObjectRef< PyOperation > PyOperationRef
Definition IRCore.h:656
MlirStringRef toMlirStringRef(const std::string &s)
Definition IRCore.h:1487
static bool attachOpTrait(const nb::object &opName, MlirDynamicOpTrait trait, PyMlirContext &context)
Definition IRCore.cpp:2556
PyObjectRef< PyModule > PyModuleRef
Definition IRCore.h:563
static MlirLogicalResult verifyTraitByMethod(MlirOperation op, void *userData, const char *methodName)
Definition IRCore.cpp:2545
static PyOperationRef getValueOwnerRef(MlirValue value)
Definition IRCore.cpp:1975
MlirBlock MLIR_PYTHON_API_EXPORTED createBlock(const nanobind::typed< nanobind::sequence, PyType > &pyArgTypes, const std::optional< nanobind::typed< nanobind::sequence, PyLocation > > &pyArgLocs)
Create a block, using the current location context if no locations are specified.
static std::vector< nb::typed< nb::object, PyType > > getValueTypes(Container &container, PyMlirContextRef &context)
Returns the list of types of the values held by container.
Definition IRCore.cpp:1389
PyWalkOrder
Traversal order for operation walk.
Definition IRCore.h:363
MLIR_PYTHON_API_EXPORTED void populateIRCore(nanobind::module_ &m)
nanobind::object classmethod(Func f, Args... args)
Helper for creating an @classmethod.
Definition IRCore.h:2025
Action
The actions performed by @newSparseTensor.
Definition Enums.h:146
Include the generated interface declarations.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
An opaque reference to a diagnostic, always owned by the diagnostics engine (context).
Definition Diagnostics.h:26
MlirLogicalResult(* verifyTrait)(MlirOperation op, void *userData)
The callback function to verify the operation.
void(* construct)(void *userData)
Optional constructor for the user data.
void(* destruct)(void *userData)
Optional destructor for the user data.
MlirLogicalResult(* verifyRegionTrait)(MlirOperation op, void *userData)
The callback function to verify the operation with access to regions.
A logical result value, essentially a boolean with named states.
Definition Support.h:121
Named MLIR attribute.
Definition IR.h:77
MlirAttribute attribute
Definition IR.h:79
MlirIdentifier name
Definition IR.h:78
A pointer to a sized fragment of a string, not necessarily null-terminated.
Definition Support.h:78
const char * data
Pointer to the first symbol.
Definition Support.h:79
size_t length
Length of the fragment.
Definition Support.h:80
Accumulates into a python string from a method that accepts an MlirStringCallback.
MlirStringCallback getCallback()
Custom exception that allows access to error diagnostic information.
Definition IRCore.h:1469
MLIRError(std::string message, std::vector< PyDiagnostic::DiagnosticInfo > &&errorDiagnostics={})
Definition IRCore.h:1470
std::vector< PyDiagnostic::DiagnosticInfo > errorDiagnostics
Definition IRCore.h:1480
static void bind(nanobind::module_ &m)
Bind the MLIRError exception class to the given module.
Definition IRCore.cpp:2922
static bool dunderContains(const std::string &attributeKind)
Definition IRCore.cpp:146
static nanobind::callable dunderGetItemNamed(const std::string &attributeKind)
Definition IRCore.cpp:151
static void dunderSetItemNamed(const std::string &attributeKind, nanobind::callable func, bool replace, bool allow_existing)
Definition IRCore.cpp:158
static void set(nanobind::object &o, bool enable)
Definition IRCore.cpp:108
RAII object that captures any error diagnostics emitted to the provided context.
Definition IRCore.h:460
std::vector< PyDiagnostic::DiagnosticInfo > take()
Definition IRCore.h:470