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/Remarks.h"
21#include "mlir-c/Support.h"
22
23#include <array>
24#include <cassert>
25#include <functional>
26#include <optional>
27#include <string>
28
29namespace nb = nanobind;
30using namespace nb::literals;
31using namespace mlir;
33
34static const char kModuleParseDocstring[] =
35 R"(Parses a module's assembly format from a string.
36
37Returns a new MlirModule or raises an MLIRError if the parsing fails.
38
39See also: https://mlir.llvm.org/docs/LangRef/
40)";
41
42static const char kDumpDocstring[] =
43 "Dumps a debug representation of the object to stderr.";
44
46 R"(Replace all uses of this value with the `with` value, except for those
47in `exceptions`. `exceptions` can be either a single operation or a list of
48operations.
49)";
50
51//------------------------------------------------------------------------------
52// Utilities.
53//------------------------------------------------------------------------------
54
55/// Local helper to compute std::hash for a value.
56template <typename T>
57static size_t hash(const T &value) {
58 return std::hash<T>{}(value);
59}
60
61static nb::object
62createCustomDialectWrapper(const std::string &dialectNamespace,
63 nb::object dialectDescriptor) {
64 auto dialectClass =
66 dialectNamespace);
67 if (!dialectClass) {
68 // Use the base class.
70 std::move(dialectDescriptor)));
71 }
72
73 // Create the custom implementation.
74 return (*dialectClass)(std::move(dialectDescriptor));
75}
76
77namespace mlir {
78namespace python {
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
563 if (mlirDiagnosticGetSeverity(diag) !=
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// Constructs a new object of type T in-place on the Python heap, returning a
936// PyObjectRef to it, loosely analogous to std::make_shared<T>().
937template <typename T, class... Args>
938PyObjectRef<T> makeObjectRef(Args &&...args) {
939 nb::handle type = nb::type<T>();
940 nb::object instance = nb::inst_alloc(type);
941 T *ptr = nb::inst_ptr<T>(instance);
942 new (ptr) T(std::forward<Args>(args)...);
943 nb::inst_mark_ready(instance);
944 return PyObjectRef<T>(ptr, std::move(instance));
945}
946} // namespace
947
948PyOperationRef PyOperation::createInstance(PyMlirContextRef contextRef,
949 MlirOperation operation,
950 nb::object parentKeepAlive) {
951 // Create.
952 PyOperationRef unownedOperation =
953 makeObjectRef<PyOperation>(std::move(contextRef), operation);
954 unownedOperation->handle = unownedOperation.getObject();
955 if (parentKeepAlive) {
956 unownedOperation->parentKeepAlive = std::move(parentKeepAlive);
957 }
958 return unownedOperation;
959}
960
962 MlirOperation operation,
963 nb::object parentKeepAlive) {
964 return createInstance(std::move(contextRef), operation,
965 std::move(parentKeepAlive));
966}
967
969 MlirOperation operation,
970 nb::object parentKeepAlive) {
971 PyOperationRef created = createInstance(std::move(contextRef), operation,
972 std::move(parentKeepAlive));
973 created->attached = false;
974 return created;
975}
976
978 const std::string &sourceStr,
979 const std::string &sourceName) {
980 PyMlirContext::ErrorCapture errors(contextRef);
981 MlirOperation op =
982 mlirOperationCreateParse(contextRef->get(), toMlirStringRef(sourceStr),
983 toMlirStringRef(sourceName));
984 if (mlirOperationIsNull(op))
985 throw MLIRError("Unable to parse operation assembly", errors.take());
986 return PyOperation::createDetached(std::move(contextRef), op);
987}
988
991 setDetached();
992 parentKeepAlive = nb::object();
993}
994
995MlirOperation PyOperation::get() const {
996 checkValid();
997 return operation;
998}
1001 return PyOperationRef(this, nb::borrow<nb::object>(handle));
1002}
1003
1004void PyOperation::setAttached(const nb::object &parent) {
1005 assert(!attached && "operation already attached");
1006 attached = true;
1007}
1008
1010 assert(attached && "operation already detached");
1011 attached = false;
1012}
1013
1014void PyOperation::checkValid() const {
1015 if (!valid) {
1016 throw std::runtime_error("the operation has been invalidated");
1017 }
1018}
1019
1020void PyOperationBase::print(std::optional<int64_t> largeElementsLimit,
1021 std::optional<int64_t> largeResourceLimit,
1022 bool enableDebugInfo, bool prettyDebugInfo,
1023 bool printGenericOpForm, bool useLocalScope,
1024 bool useNameLocAsPrefix, bool assumeVerified,
1025 nb::object fileObject, bool binary,
1026 bool skipRegions) {
1027 PyOperation &operation = getOperation();
1028 operation.checkValid();
1029 if (fileObject.is_none())
1030 fileObject = nb::module_::import_("sys").attr("stdout");
1031
1032 MlirOpPrintingFlags flags = mlirOpPrintingFlagsCreate();
1033 if (largeElementsLimit)
1034 mlirOpPrintingFlagsElideLargeElementsAttrs(flags, *largeElementsLimit);
1035 if (largeResourceLimit)
1036 mlirOpPrintingFlagsElideLargeResourceString(flags, *largeResourceLimit);
1037 if (enableDebugInfo)
1038 mlirOpPrintingFlagsEnableDebugInfo(flags, /*enable=*/true,
1039 /*prettyForm=*/prettyDebugInfo);
1040 if (printGenericOpForm)
1042 if (useLocalScope)
1044 if (assumeVerified)
1046 if (skipRegions)
1048 if (useNameLocAsPrefix)
1050
1051 PyFileAccumulator accum(fileObject, binary);
1052 mlirOperationPrintWithFlags(operation, flags, accum.getCallback(),
1053 accum.getUserData());
1055}
1056
1057void PyOperationBase::print(PyAsmState &state, nb::object fileObject,
1058 bool binary) {
1059 PyOperation &operation = getOperation();
1060 operation.checkValid();
1061 if (fileObject.is_none())
1062 fileObject = nb::module_::import_("sys").attr("stdout");
1063 PyFileAccumulator accum(fileObject, binary);
1064 mlirOperationPrintWithState(operation, state.get(), accum.getCallback(),
1065 accum.getUserData());
1066}
1067
1068void PyOperationBase::writeBytecode(const nb::object &fileOrStringObject,
1069 std::optional<int64_t> bytecodeVersion) {
1070 PyOperation &operation = getOperation();
1071 operation.checkValid();
1072 PyFileAccumulator accum(fileOrStringObject, /*binary=*/true);
1073
1074 if (!bytecodeVersion.has_value())
1075 return mlirOperationWriteBytecode(operation, accum.getCallback(),
1076 accum.getUserData());
1077
1078 MlirBytecodeWriterConfig config = mlirBytecodeWriterConfigCreate();
1079 mlirBytecodeWriterConfigDesiredEmitVersion(config, *bytecodeVersion);
1081 operation, config, accum.getCallback(), accum.getUserData());
1084 throw nb::value_error(
1085 join("Unable to honor desired bytecode version ", *bytecodeVersion)
1086 .c_str());
1087}
1088
1089void PyOperationBase::walk(std::function<PyWalkResult(MlirOperation)> callback,
1090 PyWalkOrder walkOrder) {
1091 PyOperation &operation = getOperation();
1092 operation.checkValid();
1093 struct UserData {
1094 std::function<PyWalkResult(MlirOperation)> callback;
1095 bool gotException;
1096 std::string exceptionWhat;
1097 nb::object exceptionType;
1098 };
1099 UserData userData{callback, false, {}, {}};
1100 MlirOperationWalkCallback walkCallback = [](MlirOperation op,
1101 void *userData) {
1102 UserData *calleeUserData = static_cast<UserData *>(userData);
1103 try {
1104 return static_cast<MlirWalkResult>((calleeUserData->callback)(op));
1105 } catch (nb::python_error &e) {
1106 calleeUserData->gotException = true;
1107 calleeUserData->exceptionWhat = std::string(e.what());
1108 calleeUserData->exceptionType = nb::borrow(e.type());
1109 return MlirWalkResult::MlirWalkResultInterrupt;
1110 }
1111 };
1112 mlirOperationWalk(operation, walkCallback, &userData,
1113 static_cast<MlirWalkOrder>(walkOrder));
1114 if (userData.gotException) {
1115 std::string message("Exception raised in callback: ");
1116 message.append(userData.exceptionWhat);
1117 throw std::runtime_error(message);
1118 }
1119}
1120
1121nb::object PyOperationBase::getAsm(bool binary,
1122 std::optional<int64_t> largeElementsLimit,
1123 std::optional<int64_t> largeResourceLimit,
1124 bool enableDebugInfo, bool prettyDebugInfo,
1125 bool printGenericOpForm, bool useLocalScope,
1126 bool useNameLocAsPrefix, bool assumeVerified,
1127 bool skipRegions) {
1128 nb::object fileObject;
1129 if (binary) {
1130 fileObject = nb::module_::import_("io").attr("BytesIO")();
1131 } else {
1132 fileObject = nb::module_::import_("io").attr("StringIO")();
1133 }
1134 print(/*largeElementsLimit=*/largeElementsLimit,
1135 /*largeResourceLimit=*/largeResourceLimit,
1136 /*enableDebugInfo=*/enableDebugInfo,
1137 /*prettyDebugInfo=*/prettyDebugInfo,
1138 /*printGenericOpForm=*/printGenericOpForm,
1139 /*useLocalScope=*/useLocalScope,
1140 /*useNameLocAsPrefix=*/useNameLocAsPrefix,
1141 /*assumeVerified=*/assumeVerified,
1142 /*fileObject=*/fileObject,
1143 /*binary=*/binary,
1144 /*skipRegions=*/skipRegions);
1145
1146 return fileObject.attr("getvalue")();
1147}
1148
1150 PyOperation &operation = getOperation();
1151 PyOperation &otherOp = other.getOperation();
1152 operation.checkValid();
1153 otherOp.checkValid();
1154 mlirOperationMoveAfter(operation, otherOp);
1155 operation.parentKeepAlive = otherOp.parentKeepAlive;
1156}
1157
1159 PyOperation &operation = getOperation();
1160 PyOperation &otherOp = other.getOperation();
1161 operation.checkValid();
1162 otherOp.checkValid();
1163 mlirOperationMoveBefore(operation, otherOp);
1164 operation.parentKeepAlive = otherOp.parentKeepAlive;
1165}
1166
1168 PyOperation &operation = getOperation();
1169 PyOperation &otherOp = other.getOperation();
1170 operation.checkValid();
1171 otherOp.checkValid();
1172 return mlirOperationIsBeforeInBlock(operation, otherOp);
1173}
1174
1176 PyOperation &op = getOperation();
1179 throw MLIRError("Verification failed", errors.take());
1180 return true;
1181}
1182
1183std::optional<PyOperationRef> PyOperation::getParentOperation() {
1184 checkValid();
1185 if (!isAttached())
1186 throw nb::value_error("Detached operations have no parent");
1187 MlirOperation operation = mlirOperationGetParentOperation(get());
1188 if (mlirOperationIsNull(operation))
1189 return {};
1190 return PyOperation::forOperation(getContext(), operation);
1191}
1192
1194 checkValid();
1195 std::optional<PyOperationRef> parentOperation = getParentOperation();
1196 MlirBlock block = mlirOperationGetBlock(get());
1197 assert(!mlirBlockIsNull(block) && "Attached operation has null parent");
1198 assert(parentOperation && "Operation has no parent");
1199 return PyBlock{std::move(*parentOperation), block};
1200}
1201
1203 checkValid();
1204 return nb::steal<nb::object>(mlirPythonOperationToCapsule(get()));
1205}
1206
1207nb::object PyOperation::createFromCapsule(const nb::object &capsule) {
1208 MlirOperation rawOperation = mlirPythonCapsuleToOperation(capsule.ptr());
1209 if (mlirOperationIsNull(rawOperation))
1210 throw nb::python_error();
1211 MlirContext rawCtxt = mlirOperationGetContext(rawOperation);
1212 return forOperation(PyMlirContext::forContext(rawCtxt), rawOperation)
1213 .releaseObject();
1214}
1215
1216static void maybeInsertOperation(PyOperationRef &op,
1217 const nb::object &maybeIp) {
1218 // InsertPoint active?
1219 if (!maybeIp.is(nb::cast(false))) {
1220 PyInsertionPoint *ip;
1221 if (maybeIp.is_none()) {
1223 } else {
1224 ip = nb::cast<PyInsertionPoint *>(maybeIp);
1225 }
1226 if (ip)
1227 ip->insert(*op.get());
1228 }
1229}
1230
1231nb::object PyOperation::create(std::string_view name,
1232 std::optional<std::vector<PyType *>> results,
1233 const MlirValue *operands, size_t numOperands,
1234 std::optional<nb::dict> attributes,
1235 std::optional<std::vector<PyBlock *>> successors,
1236 int regions, PyLocation &location,
1237 const nb::object &maybeIp, bool inferType) {
1238 std::vector<MlirType> mlirResults;
1239 std::vector<MlirBlock> mlirSuccessors;
1240 std::vector<std::pair<std::string, MlirAttribute>> mlirAttributes;
1241
1242 // General parameter validation.
1243 if (regions < 0)
1244 throw nb::value_error("number of regions must be >= 0");
1245
1246 // Unpack/validate results.
1247 if (results) {
1248 mlirResults.reserve(results->size());
1249 for (PyType *result : *results) {
1250 // TODO: Verify result type originate from the same context.
1251 if (!result)
1252 throw nb::value_error("result type cannot be None");
1253 mlirResults.push_back(*result);
1254 }
1255 }
1256 // Unpack/validate attributes.
1257 if (attributes) {
1258 mlirAttributes.reserve(attributes->size());
1259 for (std::pair<nb::handle, nb::handle> it : *attributes) {
1260 std::string key;
1261 try {
1262 key = nb::cast<std::string>(it.first);
1263 } catch (nb::cast_error &err) {
1264 std::string msg = join("Invalid attribute key (not a string) when "
1265 "attempting to create the operation \"",
1266 name, "\" (", err.what(), ")");
1267 throw nb::type_error(msg.c_str());
1268 }
1269 try {
1270 auto &attribute = nb::cast<PyAttribute &>(it.second);
1271 // TODO: Verify attribute originates from the same context.
1272 mlirAttributes.emplace_back(std::move(key), attribute);
1273 } catch (std::exception &err) {
1274 if (it.second.is_none()) {
1275 std::string msg = join(
1276 "Found an invalid (`None`?) attribute value for the key \"", key,
1277 "\" when attempting to create the operation \"", name, "\"");
1278 throw std::runtime_error(msg);
1279 }
1280 std::string msg = join("Invalid attribute value for the key \"", key,
1281 "\" when attempting to create the operation \"",
1282 name, "\" (", err.what(), ")");
1283 throw nb::type_error(msg.c_str());
1284 }
1285 }
1286 }
1287 // Unpack/validate successors.
1288 if (successors) {
1289 mlirSuccessors.reserve(successors->size());
1290 for (PyBlock *successor : *successors) {
1291 // TODO: Verify successor originate from the same context.
1292 if (!successor)
1293 throw nb::value_error("successor block cannot be None");
1294 mlirSuccessors.push_back(successor->get());
1295 }
1296 }
1297
1298 // Apply unpacked/validated to the operation state. Beyond this
1299 // point, exceptions cannot be thrown or else the state will leak.
1300 MlirOperationState state =
1301 mlirOperationStateGet(toMlirStringRef(name), location);
1302 if (numOperands > 0)
1303 mlirOperationStateAddOperands(&state, numOperands, operands);
1304 state.enableResultTypeInference = inferType;
1305 if (!mlirResults.empty())
1306 mlirOperationStateAddResults(&state, mlirResults.size(),
1307 mlirResults.data());
1308 if (!mlirAttributes.empty()) {
1309 // Note that the attribute names directly reference bytes in
1310 // mlirAttributes, so that vector must not be changed from here
1311 // on.
1312 std::vector<MlirNamedAttribute> mlirNamedAttributes;
1313 mlirNamedAttributes.reserve(mlirAttributes.size());
1314 for (const std::pair<std::string, MlirAttribute> &it : mlirAttributes)
1315 mlirNamedAttributes.push_back(mlirNamedAttributeGet(
1317 toMlirStringRef(it.first)),
1318 it.second));
1319 mlirOperationStateAddAttributes(&state, mlirNamedAttributes.size(),
1320 mlirNamedAttributes.data());
1321 }
1322 if (!mlirSuccessors.empty())
1323 mlirOperationStateAddSuccessors(&state, mlirSuccessors.size(),
1324 mlirSuccessors.data());
1325 if (regions) {
1326 std::vector<MlirRegion> mlirRegions;
1327 mlirRegions.resize(regions);
1328 for (int i = 0; i < regions; ++i)
1329 mlirRegions[i] = mlirRegionCreate();
1330 mlirOperationStateAddOwnedRegions(&state, mlirRegions.size(),
1331 mlirRegions.data());
1332 }
1333
1334 // Construct the operation.
1335 PyMlirContext::ErrorCapture errors(location.getContext());
1336 MlirOperation operation = mlirOperationCreate(&state);
1337 if (!operation.ptr)
1338 throw MLIRError("Operation creation failed", errors.take());
1339 PyOperationRef created =
1340 PyOperation::createDetached(location.getContext(), operation);
1341 maybeInsertOperation(created, maybeIp);
1342
1343 return created.getObject();
1344}
1345
1346nb::object PyOperation::clone(const nb::object &maybeIp) {
1347 MlirOperation clonedOperation = mlirOperationClone(operation);
1348 PyOperationRef cloned =
1349 PyOperation::createDetached(getContext(), clonedOperation);
1350 maybeInsertOperation(cloned, maybeIp);
1351
1352 return cloned->createOpView();
1353}
1354
1355nb::object PyOperation::createOpView() {
1356 checkValid();
1357 MlirIdentifier ident = mlirOperationGetName(get());
1358 MlirStringRef identStr = mlirIdentifierStr(ident);
1359 auto operationCls = PyGlobals::get().lookupOperationClass(
1360 std::string_view(identStr.data, identStr.length));
1361 if (operationCls)
1362 return PyOpView::constructDerived(*operationCls, getRef().getObject());
1363 return nb::cast(PyOpView(getRef().getObject()));
1364}
1365
1366void PyOperation::erase() {
1368 setInvalid();
1369 mlirOperationDestroy(operation);
1370}
1371
1372void PyOpResult::bindDerived(ClassTy &c) {
1373 c.def_prop_ro(
1374 "owner",
1375 [](PyOpResult &self) -> nb::typed<nb::object, PyOpView> {
1376 assert(mlirOperationEqual(self.getParentOperation()->get(),
1377 mlirOpResultGetOwner(self.get())) &&
1378 "expected the owner of the value in Python to match that in "
1379 "the IR");
1380 return self.getParentOperation()->createOpView();
1381 },
1382 "Returns the operation that produces this result.");
1383 c.def_prop_ro(
1384 "result_number",
1385 [](PyOpResult &self) { return mlirOpResultGetResultNumber(self.get()); },
1386 "Returns the position of this result in the operation's result list.");
1388
1389/// Returns the list of types of the values held by container.
1390template <typename Container>
1391static std::vector<nb::typed<nb::object, PyType>>
1392getValueTypes(Container &container, PyMlirContextRef &context) {
1393 std::vector<nb::typed<nb::object, PyType>> result;
1394 result.reserve(container.size());
1395 for (int i = 0, e = container.size(); i < e; ++i) {
1396 result.push_back(PyType(context->getRef(),
1397 mlirValueGetType(container.getElement(i).get()))
1399 }
1400 return result;
1401}
1402
1404 intptr_t length, intptr_t step)
1405 : Sliceable(startIndex,
1407 : length,
1408 step),
1409 operation(std::move(operation)) {}
1410
1411void PyOpResultList::bindDerived(ClassTy &c) {
1412 c.def_prop_ro(
1413 "types",
1414 [](PyOpResultList &self) {
1415 return getValueTypes(self, self.operation->getContext());
1416 },
1417 "Returns a list of types for all results in this result list.");
1418 c.def_prop_ro(
1419 "owner",
1420 [](PyOpResultList &self) -> nb::typed<nb::object, PyOpView> {
1421 return self.operation->createOpView();
1422 },
1423 "Returns the operation that owns this result list.");
1424}
1425
1426intptr_t PyOpResultList::getRawNumElements() {
1427 operation->checkValid();
1428 return mlirOperationGetNumResults(operation->get());
1429}
1430
1431PyOpResult PyOpResultList::getRawElement(intptr_t index) {
1432 PyValue value(operation, mlirOperationGetResult(operation->get(), index));
1433 return PyOpResult(value);
1434}
1435
1436PyOpResultList PyOpResultList::slice(intptr_t startIndex, intptr_t length,
1437 intptr_t step) const {
1438 return PyOpResultList(operation, startIndex, length, step);
1439}
1441//------------------------------------------------------------------------------
1442// PyOpView
1443//------------------------------------------------------------------------------
1444
1445static void populateResultTypes(std::string_view name,
1446 nb::sequence resultTypeList,
1447 const nb::object &resultSegmentSpecObj,
1448 std::vector<int32_t> &resultSegmentLengths,
1449 std::vector<PyType *> &resultTypes) {
1450 resultTypes.reserve(nb::len(resultTypeList));
1451 if (resultSegmentSpecObj.is_none()) {
1452 // Non-variadic result unpacking.
1453 size_t index = 0;
1454 for (nb::handle resultType : resultTypeList) {
1455 try {
1456 resultTypes.push_back(nb::cast<PyType *>(resultType));
1457 if (!resultTypes.back())
1458 throw nb::cast_error();
1459 } catch (nb::cast_error &err) {
1460 throw nb::value_error(join("Result ", index, " of operation \"", name,
1461 "\" must be a Type (", err.what(), ")")
1462 .c_str());
1463 }
1464 ++index;
1465 }
1466 } else {
1467 // Sized result unpacking.
1468 auto resultSegmentSpec = nb::cast<std::vector<int>>(resultSegmentSpecObj);
1469 if (resultSegmentSpec.size() != nb::len(resultTypeList)) {
1470 throw nb::value_error(
1471 join("Operation \"", name, "\" requires ", resultSegmentSpec.size(),
1472 " result segments but was provided ", nb::len(resultTypeList))
1473 .c_str());
1474 }
1475 resultSegmentLengths.reserve(nb::len(resultTypeList));
1476 for (size_t i = 0, e = resultSegmentSpec.size(); i < e; ++i) {
1477 int segmentSpec = resultSegmentSpec[i];
1478 if (segmentSpec == 1 || segmentSpec == 0) {
1479 // Unpack unary element.
1480 try {
1481 auto *resultType = nb::cast<PyType *>(resultTypeList[i]);
1482 if (resultType) {
1483 resultTypes.push_back(resultType);
1484 resultSegmentLengths.push_back(1);
1485 } else if (segmentSpec == 0) {
1486 // Allowed to be optional.
1487 resultSegmentLengths.push_back(0);
1488 } else {
1489 throw nb::value_error(
1490 join("Result ", i, " of operation \"", name,
1491 "\" must be a Type (was None and result is not optional)")
1492 .c_str());
1493 }
1494 } catch (nb::cast_error &err) {
1495 throw nb::value_error(join("Result ", i, " of operation \"", name,
1496 "\" must be a Type (", err.what(), ")")
1497 .c_str());
1498 }
1499 } else if (segmentSpec == -1) {
1500 // Unpack sequence by appending.
1501 try {
1502 if (resultTypeList[i].is_none()) {
1503 // Treat it as an empty list.
1504 resultSegmentLengths.push_back(0);
1505 } else {
1506 // Unpack the list.
1507 auto segment = nb::cast<nb::sequence>(resultTypeList[i]);
1508 for (nb::handle segmentItem : segment) {
1509 resultTypes.push_back(nb::cast<PyType *>(segmentItem));
1510 if (!resultTypes.back()) {
1511 throw nb::type_error("contained a None item");
1512 }
1513 }
1514 resultSegmentLengths.push_back(nb::len(segment));
1515 }
1516 } catch (std::exception &err) {
1517 // NOTE: Sloppy to be using a catch-all here, but there are at least
1518 // three different unrelated exceptions that can be thrown in the
1519 // above "casts". Just keep the scope above small and catch them all.
1520 throw nb::value_error(join("Result ", i, " of operation \"", name,
1521 "\" must be a Sequence of Types (",
1522 err.what(), ")")
1523 .c_str());
1524 }
1525 } else {
1526 throw nb::value_error("Unexpected segment spec");
1528 }
1529 }
1530}
1531
1532MlirValue getUniqueResult(MlirOperation operation) {
1533 auto numResults = mlirOperationGetNumResults(operation);
1534 if (numResults != 1) {
1535 auto name = mlirIdentifierStr(mlirOperationGetName(operation));
1536 throw nb::value_error(
1537 join("Cannot call .result on operation ",
1538 std::string_view(name.data, name.length), " which has ",
1539 numResults,
1540 " results (it is only valid for operations with a "
1541 "single result)")
1542 .c_str());
1543 }
1544 return mlirOperationGetResult(operation, 0);
1545}
1546
1547static MlirValue getOpResultOrValue(nb::handle operand) {
1548 if (operand.is_none()) {
1549 throw nb::value_error("contained a None item");
1550 }
1551 PyOperationBase *op;
1552 if (nb::try_cast<PyOperationBase *>(operand, op)) {
1553 return getUniqueResult(op->getOperation());
1554 }
1555 PyOpResultList *opResultList;
1556 if (nb::try_cast<PyOpResultList *>(operand, opResultList)) {
1557 return getUniqueResult(opResultList->getOperation()->get());
1558 }
1559 PyValue *value;
1560 if (nb::try_cast<PyValue *>(operand, value)) {
1561 return value->get();
1562 }
1563 throw nb::value_error("is not a Value");
1564}
1565
1566nb::typed<nb::object, PyOperation> PyOpView::buildGeneric(
1567 std::string_view name, std::tuple<int, bool> opRegionSpec,
1568 nb::object operandSegmentSpecObj, nb::object resultSegmentSpecObj,
1569 std::optional<nb::sequence> resultTypeList, nb::sequence operandList,
1570 std::optional<nb::dict> attributes,
1571 std::optional<std::vector<PyBlock *>> successors,
1572 std::optional<int> regions, PyLocation &location,
1573 const nb::object &maybeIp) {
1574 PyMlirContextRef context = location.getContext();
1575
1576 // Class level operation construction metadata.
1577 // Operand and result segment specs are either none, which does no
1578 // variadic unpacking, or a list of ints with segment sizes, where each
1579 // element is either a positive number (typically 1 for a scalar) or -1 to
1580 // indicate that it is derived from the length of the same-indexed operand
1581 // or result (implying that it is a list at that position).
1582 std::vector<int32_t> operandSegmentLengths;
1583 std::vector<int32_t> resultSegmentLengths;
1584
1585 // Validate/determine region count.
1586 int opMinRegionCount = std::get<0>(opRegionSpec);
1587 bool opHasNoVariadicRegions = std::get<1>(opRegionSpec);
1588 if (!regions) {
1589 regions = opMinRegionCount;
1590 }
1591 if (*regions < opMinRegionCount) {
1592 throw nb::value_error(join("Operation \"", name,
1593 "\" requires a minimum of ", opMinRegionCount,
1594 " regions but was built with regions=", *regions)
1595 .c_str());
1596 }
1597 if (opHasNoVariadicRegions && *regions > opMinRegionCount) {
1598 throw nb::value_error(join("Operation \"", name,
1599 "\" requires a maximum of ", opMinRegionCount,
1600 " regions but was built with regions=", *regions)
1601 .c_str());
1602 }
1603
1604 // Unpack results.
1605 std::vector<PyType *> resultTypes;
1606 if (resultTypeList.has_value()) {
1607 populateResultTypes(name, *resultTypeList, resultSegmentSpecObj,
1608 resultSegmentLengths, resultTypes);
1609 }
1610
1611 // Unpack operands.
1612 std::vector<MlirValue> operands;
1613 operands.reserve(operands.size());
1614 size_t index = 0;
1615 if (operandSegmentSpecObj.is_none()) {
1616 // Non-sized operand unpacking.
1617 for (nb::handle operand : operandList) {
1618 try {
1619 operands.push_back(getOpResultOrValue(operand));
1620 } catch (nb::builtin_exception &err) {
1621 throw nb::value_error(join("Operand ", index, " of operation \"", name,
1622 "\" must be a Value (", err.what(), ")")
1623 .c_str());
1624 }
1625 ++index;
1626 }
1627 } else {
1628 // Sized operand unpacking.
1629 auto operandSegmentSpec = nb::cast<std::vector<int>>(operandSegmentSpecObj);
1630 if (operandSegmentSpec.size() != nb::len(operandList)) {
1631 throw nb::value_error(
1632 join("Operation \"", name, "\" requires ", operandSegmentSpec.size(),
1633 "operand segments but was provided ", nb::len(operandList))
1634 .c_str());
1635 }
1636 operandSegmentLengths.reserve(nb::len(operandList));
1637 for (size_t i = 0, e = operandSegmentSpec.size(); i < e; ++i) {
1638 int segmentSpec = operandSegmentSpec[i];
1639 if (segmentSpec == 1 || segmentSpec == 0) {
1640 // Unpack unary element.
1641 const nanobind::handle operand = operandList[i];
1642 if (!operand.is_none()) {
1643 try {
1644 operands.push_back(getOpResultOrValue(operand));
1645 } catch (nb::builtin_exception &err) {
1646 throw nb::value_error(join("Operand ", i, " of operation \"", name,
1647 "\" must be a Value (", err.what(), ")")
1648 .c_str());
1649 }
1650
1651 operandSegmentLengths.push_back(1);
1652 } else if (segmentSpec == 0) {
1653 // Allowed to be optional.
1654 operandSegmentLengths.push_back(0);
1655 } else {
1656 throw nb::value_error(
1657 join("Operand ", i, " of operation \"", name,
1658 "\" must be a Value (was None and operand is not optional)")
1659 .c_str());
1660 }
1661 } else if (segmentSpec == -1) {
1662 // Unpack sequence by appending.
1663 try {
1664 if (operandList[i].is_none()) {
1665 // Treat it as an empty list.
1666 operandSegmentLengths.push_back(0);
1667 } else {
1668 // Unpack the list.
1669 auto segment = nb::cast<nb::sequence>(operandList[i]);
1670 for (nb::handle segmentItem : segment) {
1671 operands.push_back(getOpResultOrValue(segmentItem));
1672 }
1673 operandSegmentLengths.push_back(nb::len(segment));
1674 }
1675 } catch (std::exception &err) {
1676 // NOTE: Sloppy to be using a catch-all here, but there are at least
1677 // three different unrelated exceptions that can be thrown in the
1678 // above "casts". Just keep the scope above small and catch them all.
1679 throw nb::value_error(join("Operand ", i, " of operation \"", name,
1680 "\" must be a Sequence of Values (",
1681 err.what(), ")")
1682 .c_str());
1683 }
1684 } else {
1685 throw nb::value_error("Unexpected segment spec");
1686 }
1687 }
1688 }
1689
1690 // Merge operand/result segment lengths into attributes if needed.
1691 if (!operandSegmentLengths.empty() || !resultSegmentLengths.empty()) {
1692 // Dup.
1693 if (attributes) {
1694 attributes = nb::dict(*attributes);
1695 } else {
1696 attributes = nb::dict();
1697 }
1698 if (attributes->contains("resultSegmentSizes") ||
1699 attributes->contains("operandSegmentSizes")) {
1700 throw nb::value_error("Manually setting a 'resultSegmentSizes' or "
1701 "'operandSegmentSizes' attribute is unsupported. "
1702 "Use Operation.create for such low-level access.");
1703 }
1704
1705 // Add resultSegmentSizes attribute.
1706 if (!resultSegmentLengths.empty()) {
1707 MlirAttribute segmentLengthAttr =
1708 mlirDenseI32ArrayGet(context->get(), resultSegmentLengths.size(),
1709 resultSegmentLengths.data());
1710 (*attributes)["resultSegmentSizes"] =
1711 PyAttribute(context, segmentLengthAttr);
1712 }
1713
1714 // Add operandSegmentSizes attribute.
1715 if (!operandSegmentLengths.empty()) {
1716 MlirAttribute segmentLengthAttr =
1717 mlirDenseI32ArrayGet(context->get(), operandSegmentLengths.size(),
1718 operandSegmentLengths.data());
1719 (*attributes)["operandSegmentSizes"] =
1720 PyAttribute(context, segmentLengthAttr);
1721 }
1722 }
1723
1724 // Delegate to create.
1725 return PyOperation::create(name,
1726 /*results=*/std::move(resultTypes),
1727 /*operands=*/operands.data(),
1728 /*numOperands=*/operands.size(),
1729 /*attributes=*/std::move(attributes),
1730 /*successors=*/std::move(successors),
1731 /*regions=*/*regions, location, maybeIp,
1732 !resultTypeList);
1733}
1734
1735nb::object PyOpView::constructDerived(const nb::object &cls,
1736 const nb::object &operation) {
1737 nb::handle opViewType = nb::type<PyOpView>();
1738 nb::object instance = cls.attr("__new__")(cls);
1739 opViewType.attr("__init__")(instance, operation);
1740 return instance;
1741}
1742
1743PyOpView::PyOpView(const nb::object &operationObject)
1744 // Casting through the PyOperationBase base-class and then back to the
1745 // Operation lets us accept any PyOperationBase subclass.
1746 : operation(nb::cast<PyOperationBase &>(operationObject).getOperation()),
1747 operationObject(operation.getRef().getObject()) {}
1749//------------------------------------------------------------------------------
1750// PyAsmState
1751//------------------------------------------------------------------------------
1752
1753PyAsmState::PyAsmState(MlirValue value, bool useLocalScope) {
1754 flags = mlirOpPrintingFlagsCreate();
1755 // The OpPrintingFlags are not exposed Python side, create locally and
1756 // associate lifetime with the state.
1757 if (useLocalScope)
1759 state = mlirAsmStateCreateForValue(value, flags);
1760}
1761
1762PyAsmState::PyAsmState(PyOperationBase &operation, bool useLocalScope) {
1763 flags = mlirOpPrintingFlagsCreate();
1764 // The OpPrintingFlags are not exposed Python side, create locally and
1765 // associate lifetime with the state.
1766 if (useLocalScope)
1768 state = mlirAsmStateCreateForOperation(operation.getOperation().get(), flags);
1769}
1771//------------------------------------------------------------------------------
1772// PyInsertionPoint.
1773//------------------------------------------------------------------------------
1774
1775PyInsertionPoint::PyInsertionPoint(const PyBlock &block) : block(block) {}
1778 : refOperation(beforeOperationBase.getOperation().getRef()),
1779 block((*refOperation)->getBlock()) {}
1780
1782 : refOperation(beforeOperationRef), block((*refOperation)->getBlock()) {}
1783
1784void PyInsertionPoint::insert(PyOperationBase &operationBase) {
1785 PyOperation &operation = operationBase.getOperation();
1786 if (operation.isAttached())
1787 throw nb::value_error(
1788 "Attempt to insert operation that is already attached");
1789 block.getParentOperation()->checkValid();
1790 MlirOperation beforeOp = {nullptr};
1791 if (refOperation) {
1792 // Insert before operation.
1793 (*refOperation)->checkValid();
1794 beforeOp = (*refOperation)->get();
1795 } else {
1796 // Insert at end (before null) is only valid if the block does not
1797 // already end in a known terminator (violating this will cause assertion
1798 // failures later).
1799 if (!mlirOperationIsNull(mlirBlockGetTerminator(block.get()))) {
1800 throw nb::index_error("Cannot insert operation at the end of a block "
1801 "that already has a terminator. Did you mean to "
1802 "use 'InsertionPoint.at_block_terminator(block)' "
1803 "versus 'InsertionPoint(block)'?");
1804 }
1806 mlirBlockInsertOwnedOperationBefore(block.get(), beforeOp, operation);
1807 operation.setAttached();
1808}
1809
1811 MlirOperation firstOp = mlirBlockGetFirstOperation(block.get());
1812 if (mlirOperationIsNull(firstOp)) {
1813 // Just insert at end.
1814 return PyInsertionPoint(block);
1815 }
1816
1817 // Insert before first op.
1819 block.getParentOperation()->getContext(), firstOp);
1820 return PyInsertionPoint{block, std::move(firstOpRef)};
1821}
1822
1824 MlirOperation terminator = mlirBlockGetTerminator(block.get());
1825 if (mlirOperationIsNull(terminator))
1826 throw nb::value_error("Block has no terminator");
1828 block.getParentOperation()->getContext(), terminator);
1829 return PyInsertionPoint{block, std::move(terminatorOpRef)};
1830}
1831
1833 PyOperation &operation = op.getOperation();
1834 PyBlock block = operation.getBlock();
1835 MlirOperation nextOperation = mlirOperationGetNextInBlock(operation);
1836 if (mlirOperationIsNull(nextOperation))
1837 return PyInsertionPoint(block);
1839 block.getParentOperation()->getContext(), nextOperation);
1840 return PyInsertionPoint{block, std::move(nextOpRef)};
1841}
1842
1843size_t PyMlirContext::getLiveModuleCount() { return liveModules.size(); }
1845nb::object PyInsertionPoint::contextEnter(nb::object insertPoint) {
1846 return PyThreadContextEntry::pushInsertionPoint(std::move(insertPoint));
1847}
1848
1849void PyInsertionPoint::contextExit(const nb::object &excType,
1850 const nb::object &excVal,
1851 const nb::object &excTb) {
1853}
1855//------------------------------------------------------------------------------
1856// PyAttribute.
1857//------------------------------------------------------------------------------
1859bool PyAttribute::operator==(const PyAttribute &other) const {
1860 return mlirAttributeEqual(attr, other.attr);
1861}
1863nb::object PyAttribute::getCapsule() {
1864 return nb::steal<nb::object>(mlirPythonAttributeToCapsule(*this));
1865}
1866
1867PyAttribute PyAttribute::createFromCapsule(const nb::object &capsule) {
1868 MlirAttribute rawAttr = mlirPythonCapsuleToAttribute(capsule.ptr());
1869 if (mlirAttributeIsNull(rawAttr))
1870 throw nb::python_error();
1871 return PyAttribute(
1873}
1874
1875nb::typed<nb::object, PyAttribute> PyAttribute::maybeDownCast() {
1876 MlirTypeID mlirTypeID = mlirAttributeGetTypeID(this->get());
1877 assert(!mlirTypeIDIsNull(mlirTypeID) &&
1878 "mlirTypeID was expected to be non-null.");
1879 std::optional<nb::callable> typeCaster = PyGlobals::get().lookupTypeCaster(
1880 mlirTypeID, mlirAttributeGetDialect(this->get()));
1881 // nb::rv_policy::move means use std::move to move the return value
1882 // contents into a new instance that will be owned by Python.
1883 nb::object thisObj = nb::cast(this, nb::rv_policy::move);
1884 if (!typeCaster)
1885 return thisObj;
1886 return typeCaster.value()(thisObj);
1887}
1889//------------------------------------------------------------------------------
1890// PyLocation::maybeDownCast.
1891//------------------------------------------------------------------------------
1892
1893nb::typed<nb::object, PyLocation> PyLocation::maybeDownCast() {
1894 MlirAttribute locAttr = mlirLocationGetAttribute(this->get());
1895 MlirTypeID mlirTypeID = mlirAttributeGetTypeID(locAttr);
1896 assert(!mlirTypeIDIsNull(mlirTypeID) &&
1897 "mlirTypeID was expected to be non-null.");
1898 std::optional<nb::callable> typeCaster = PyGlobals::get().lookupTypeCaster(
1899 mlirTypeID, mlirAttributeGetDialect(locAttr));
1900 nb::object thisObj = nb::cast(this, nb::rv_policy::move);
1901 if (!typeCaster)
1902 return thisObj;
1903 return typeCaster.value()(thisObj);
1904}
1906//------------------------------------------------------------------------------
1907// PyNamedAttribute.
1908//------------------------------------------------------------------------------
1909
1910PyNamedAttribute::PyNamedAttribute(MlirAttribute attr, std::string ownedName)
1911 : ownedName(new std::string(std::move(ownedName))) {
1914 toMlirStringRef(*this->ownedName)),
1915 attr);
1916}
1918//------------------------------------------------------------------------------
1919// PyType.
1920//------------------------------------------------------------------------------
1922bool PyType::operator==(const PyType &other) const {
1923 return mlirTypeEqual(type, other.type);
1924}
1926nb::object PyType::getCapsule() {
1927 return nb::steal<nb::object>(mlirPythonTypeToCapsule(*this));
1928}
1929
1930PyType PyType::createFromCapsule(nb::object capsule) {
1931 MlirType rawType = mlirPythonCapsuleToType(capsule.ptr());
1932 if (mlirTypeIsNull(rawType))
1933 throw nb::python_error();
1935 rawType);
1936}
1937
1938nb::typed<nb::object, PyType> PyType::maybeDownCast() {
1939 MlirTypeID mlirTypeID = mlirTypeGetTypeID(this->get());
1940 assert(!mlirTypeIDIsNull(mlirTypeID) &&
1941 "mlirTypeID was expected to be non-null.");
1942 std::optional<nb::callable> typeCaster = PyGlobals::get().lookupTypeCaster(
1943 mlirTypeID, mlirTypeGetDialect(this->get()));
1944 // nb::rv_policy::move means use std::move to move the return value
1945 // contents into a new instance that will be owned by Python.
1946 nb::object thisObj = nb::cast(this, nb::rv_policy::move);
1947 if (!typeCaster)
1948 return thisObj;
1949 return typeCaster.value()(thisObj);
1950}
1952//------------------------------------------------------------------------------
1953// PyTypeID.
1954//------------------------------------------------------------------------------
1956nb::object PyTypeID::getCapsule() {
1957 return nb::steal<nb::object>(mlirPythonTypeIDToCapsule(*this));
1958}
1959
1960PyTypeID PyTypeID::createFromCapsule(nb::object capsule) {
1961 MlirTypeID mlirTypeID = mlirPythonCapsuleToTypeID(capsule.ptr());
1962 if (mlirTypeIDIsNull(mlirTypeID))
1963 throw nb::python_error();
1964 return PyTypeID(mlirTypeID);
1965}
1966bool PyTypeID::operator==(const PyTypeID &other) const {
1967 return mlirTypeIDEqual(typeID, other.typeID);
1968}
1970//------------------------------------------------------------------------------
1971// PyValue and subclasses.
1972//------------------------------------------------------------------------------
1974nb::object PyValue::getCapsule() {
1975 return nb::steal<nb::object>(mlirPythonValueToCapsule(get()));
1976}
1977
1978static PyOperationRef getValueOwnerRef(MlirValue value) {
1979 MlirOperation owner;
1980 if (mlirValueIsAOpResult(value))
1981 owner = mlirOpResultGetOwner(value);
1982 else if (mlirValueIsABlockArgument(value))
1984 else
1985 assert(false && "Value must be an block arg or op result.");
1986 if (mlirOperationIsNull(owner))
1987 throw nb::python_error();
1988 MlirContext ctx = mlirOperationGetContext(owner);
1990}
1991
1992nb::typed<nb::object, std::variant<PyBlockArgument, PyOpResult, PyValue>>
1994 MlirType type = mlirValueGetType(get());
1995 MlirTypeID mlirTypeID = mlirTypeGetTypeID(type);
1996 assert(!mlirTypeIDIsNull(mlirTypeID) &&
1997 "mlirTypeID was expected to be non-null.");
1998 std::optional<nb::callable> valueCaster =
2000 // nb::rv_policy::move means use std::move to move the return value
2001 // contents into a new instance that will be owned by Python.
2002 nb::object thisObj;
2003 if (mlirValueIsAOpResult(value))
2004 thisObj = nb::cast<PyOpResult>(*this, nb::rv_policy::move);
2005 else if (mlirValueIsABlockArgument(value))
2006 thisObj = nb::cast<PyBlockArgument>(*this, nb::rv_policy::move);
2007 else
2008 assert(false && "Value must be an block arg or op result.");
2009 if (valueCaster)
2010 return valueCaster.value()(thisObj);
2011 return thisObj;
2012}
2013
2014PyValue PyValue::createFromCapsule(nb::object capsule) {
2015 MlirValue value = mlirPythonCapsuleToValue(capsule.ptr());
2016 if (mlirValueIsNull(value))
2017 throw nb::python_error();
2018 PyOperationRef ownerRef = getValueOwnerRef(value);
2019 return PyValue(ownerRef, value);
2020}
2022//------------------------------------------------------------------------------
2023// PySymbolTable.
2024//------------------------------------------------------------------------------
2025
2027 : operation(operation.getOperation().getRef()) {
2028 symbolTable = mlirSymbolTableCreate(operation.getOperation().get());
2029 if (mlirSymbolTableIsNull(symbolTable)) {
2030 throw nb::type_error("Operation is not a Symbol Table.");
2031 }
2032}
2033
2034nb::object PySymbolTable::dunderGetItem(const std::string &name) {
2035 operation->checkValid();
2036 MlirOperation symbol = mlirSymbolTableLookup(
2037 symbolTable, mlirStringRefCreate(name.data(), name.length()));
2038 if (mlirOperationIsNull(symbol))
2039 throw nb::key_error(
2040 join("Symbol '", name, "' not in the symbol table.").c_str());
2041
2042 return PyOperation::forOperation(operation->getContext(), symbol,
2043 operation.getObject())
2044 ->createOpView();
2045}
2046
2048 operation->checkValid();
2049 symbol.getOperation().checkValid();
2050 mlirSymbolTableErase(symbolTable, symbol.getOperation().get());
2051 // The operation is also erased, so we must invalidate it. There may be Python
2052 // references to this operation so we don't want to delete it from the list of
2053 // live operations here.
2054 symbol.getOperation().valid = false;
2055}
2056
2057void PySymbolTable::dunderDel(const std::string &name) {
2058 nb::object operation = dunderGetItem(name);
2059 erase(nb::cast<PyOperationBase &>(operation));
2060}
2061
2063 operation->checkValid();
2064 symbol.getOperation().checkValid();
2065 MlirAttribute symbolAttr = mlirOperationGetAttributeByName(
2067 if (mlirAttributeIsNull(symbolAttr))
2068 throw nb::value_error("Expected operation to have a symbol name.");
2070 symbol.getOperation().getContext(),
2071 mlirSymbolTableInsert(symbolTable, symbol.getOperation().get()));
2072}
2073
2075 // Op must already be a symbol.
2076 PyOperation &operation = symbol.getOperation();
2077 operation.checkValid();
2079 MlirAttribute existingNameAttr =
2080 mlirOperationGetAttributeByName(operation.get(), attrName);
2081 if (mlirAttributeIsNull(existingNameAttr))
2082 throw nb::value_error("Expected operation to have a symbol name.");
2083 return PyStringAttribute(symbol.getOperation().getContext(),
2084 existingNameAttr);
2085}
2086
2088 const std::string &name) {
2089 // Op must already be a symbol.
2090 PyOperation &operation = symbol.getOperation();
2091 operation.checkValid();
2093 MlirAttribute existingNameAttr =
2094 mlirOperationGetAttributeByName(operation.get(), attrName);
2095 if (mlirAttributeIsNull(existingNameAttr))
2096 throw nb::value_error("Expected operation to have a symbol name.");
2097 MlirAttribute newNameAttr =
2098 mlirStringAttrGet(operation.getContext()->get(), toMlirStringRef(name));
2099 mlirOperationSetAttributeByName(operation.get(), attrName, newNameAttr);
2100}
2101
2103 PyOperation &operation = symbol.getOperation();
2104 operation.checkValid();
2106 MlirAttribute existingVisAttr =
2107 mlirOperationGetAttributeByName(operation.get(), attrName);
2108 if (mlirAttributeIsNull(existingVisAttr))
2109 throw nb::value_error("Expected operation to have a symbol visibility.");
2110 return PyStringAttribute(symbol.getOperation().getContext(), existingVisAttr);
2111}
2112
2114 const std::string &visibility) {
2115 if (visibility != "public" && visibility != "private" &&
2116 visibility != "nested")
2117 throw nb::value_error(
2118 "Expected visibility to be 'public', 'private' or 'nested'");
2119 PyOperation &operation = symbol.getOperation();
2120 operation.checkValid();
2122 MlirAttribute existingVisAttr =
2123 mlirOperationGetAttributeByName(operation.get(), attrName);
2124 if (mlirAttributeIsNull(existingVisAttr))
2125 throw nb::value_error("Expected operation to have a symbol visibility.");
2126 MlirAttribute newVisAttr = mlirStringAttrGet(operation.getContext()->get(),
2127 toMlirStringRef(visibility));
2128 mlirOperationSetAttributeByName(operation.get(), attrName, newVisAttr);
2129}
2130
2131void PySymbolTable::replaceAllSymbolUses(const std::string &oldSymbol,
2132 const std::string &newSymbol,
2133 PyOperationBase &from) {
2134 PyOperation &fromOperation = from.getOperation();
2135 fromOperation.checkValid();
2137 toMlirStringRef(oldSymbol), toMlirStringRef(newSymbol),
2139
2140 throw nb::value_error("Symbol rename failed");
2141}
2142
2144 bool allSymUsesVisible,
2145 nb::object callback) {
2146 PyOperation &fromOperation = from.getOperation();
2147 fromOperation.checkValid();
2148 struct UserData {
2149 PyMlirContextRef context;
2150 nb::object callback;
2151 bool gotException;
2152 std::string exceptionWhat;
2153 nb::object exceptionType;
2154 };
2155 UserData userData{
2156 fromOperation.getContext(), std::move(callback), false, {}, {}};
2158 fromOperation.get(), allSymUsesVisible,
2159 [](MlirOperation foundOp, bool isVisible, void *calleeUserDataVoid) {
2160 UserData *calleeUserData = static_cast<UserData *>(calleeUserDataVoid);
2161 auto pyFoundOp =
2162 PyOperation::forOperation(calleeUserData->context, foundOp);
2163 if (calleeUserData->gotException)
2164 return;
2165 try {
2166 calleeUserData->callback(pyFoundOp.getObject(), isVisible);
2167 } catch (nb::python_error &e) {
2168 calleeUserData->gotException = true;
2169 calleeUserData->exceptionWhat = e.what();
2170 calleeUserData->exceptionType = nb::borrow(e.type());
2171 }
2172 },
2173 static_cast<void *>(&userData));
2174 if (userData.gotException) {
2175 std::string message("Exception raised in callback: ");
2176 message.append(userData.exceptionWhat);
2177 throw std::runtime_error(message);
2178 }
2179}
2180
2181void PyBlockArgument::bindDerived(ClassTy &c) {
2182 c.def_prop_ro(
2183 "owner",
2184 [](PyBlockArgument &self) {
2185 return PyBlock(self.getParentOperation(),
2187 },
2188 "Returns the block that owns this argument.");
2189 c.def_prop_ro(
2190 "arg_number",
2191 [](PyBlockArgument &self) {
2192 return mlirBlockArgumentGetArgNumber(self.get());
2193 },
2194 "Returns the position of this argument in the block's argument list.");
2195 c.def(
2196 "set_type",
2197 [](PyBlockArgument &self, PyType type) {
2198 return mlirBlockArgumentSetType(self.get(), type);
2199 },
2200 "type"_a, "Sets the type of this block argument.");
2201 c.def(
2202 "set_location",
2203 [](PyBlockArgument &self, PyLocation loc) {
2205 },
2206 "loc"_a, "Sets the location of this block argument.");
2207}
2208
2210 MlirBlock block, intptr_t startIndex,
2213 length == -1 ? mlirBlockGetNumArguments(block) : length, step),
2214 operation(std::move(operation)), block(block) {}
2215
2216void PyBlockArgumentList::bindDerived(ClassTy &c) {
2217 c.def_prop_ro(
2218 "types",
2219 [](PyBlockArgumentList &self) {
2220 return getValueTypes(self, self.operation->getContext());
2221 },
2222 "Returns a list of types for all arguments in this argument list.");
2223}
2224
2225intptr_t PyBlockArgumentList::getRawNumElements() {
2226 operation->checkValid();
2227 return mlirBlockGetNumArguments(block);
2228}
2229
2230PyBlockArgument PyBlockArgumentList::getRawElement(intptr_t pos) const {
2231 MlirValue argument = mlirBlockGetArgument(block, pos);
2232 return PyBlockArgument(operation, argument);
2233}
2234
2235PyBlockArgumentList PyBlockArgumentList::slice(intptr_t startIndex,
2237 intptr_t step) const {
2238 return PyBlockArgumentList(operation, block, startIndex, length, step);
2239}
2240
2242 intptr_t length, intptr_t step)
2243 : Sliceable(startIndex,
2245 : length,
2246 step),
2247 operation(operation) {}
2248
2251 mlirOperationSetOperand(operation->get(), index, value.get());
2252}
2253
2254void PyOpOperandList::bindDerived(ClassTy &c) {
2255 c.def("__setitem__", &PyOpOperandList::dunderSetItem, "index"_a, "value"_a,
2256 "Sets the operand at the specified index to a new value.");
2257}
2258
2259intptr_t PyOpOperandList::getRawNumElements() {
2260 operation->checkValid();
2261 return mlirOperationGetNumOperands(operation->get());
2262}
2263
2264PyValue PyOpOperandList::getRawElement(intptr_t pos) {
2265 MlirValue operand = mlirOperationGetOperand(operation->get(), pos);
2266 PyOperationRef pyOwner = getValueOwnerRef(operand);
2267 return PyValue(pyOwner, operand);
2268}
2269
2270PyOpOperandList PyOpOperandList::slice(intptr_t startIndex, intptr_t length,
2271 intptr_t step) const {
2272 return PyOpOperandList(operation, startIndex, length, step);
2273}
2275/// A list of OpOperands. Internally, these are stored as consecutive elements,
2276/// random access is cheap. The (returned) OpOperand list is associated with the
2277/// operation whose operands these are, and thus extends the lifetime of this
2278/// operation.
2279class PyOpOperands : public Sliceable<PyOpOperands, PyOpOperand> {
2280public:
2281 static constexpr const char *pyClassName = "OpOperands";
2283
2285 intptr_t length = -1, intptr_t step = 1)
2287 length == -1 ? mlirOperationGetNumOperands(operation->get())
2288 : length,
2289 step),
2290 operation(operation) {}
2291
2292private:
2293 /// Give the parent CRTP class access to hook implementations below.
2294 friend class Sliceable<PyOpOperands, PyOpOperand>;
2295
2296 intptr_t getRawNumElements() {
2297 operation->checkValid();
2298 return mlirOperationGetNumOperands(operation->get());
2299 }
2300
2301 PyOpOperand getRawElement(intptr_t pos) {
2302 MlirOpOperand opOperand = mlirOperationGetOpOperand(operation->get(), pos);
2303 return PyOpOperand(opOperand);
2304 }
2305
2307 return PyOpOperands(operation, startIndex, length, step);
2309
2310 PyOperationRef operation;
2311};
2312
2314 intptr_t length, intptr_t step)
2315 : Sliceable(startIndex,
2317 : length,
2318 step),
2319 operation(operation) {}
2320
2323 mlirOperationSetSuccessor(operation->get(), index, block.get());
2324}
2325
2326void PyOpSuccessors::bindDerived(ClassTy &c) {
2327 c.def("__setitem__", &PyOpSuccessors::dunderSetItem, "index"_a, "block"_a,
2328 "Sets the successor block at the specified index.");
2329}
2330
2331intptr_t PyOpSuccessors::getRawNumElements() {
2332 operation->checkValid();
2333 return mlirOperationGetNumSuccessors(operation->get());
2334}
2335
2336PyBlock PyOpSuccessors::getRawElement(intptr_t pos) {
2337 MlirBlock block = mlirOperationGetSuccessor(operation->get(), pos);
2338 return PyBlock(operation, block);
2339}
2340
2342 intptr_t step) const {
2343 return PyOpSuccessors(operation, startIndex, length, step);
2344}
2345
2347 intptr_t startIndex, intptr_t length,
2348 intptr_t step)
2349 : Sliceable(startIndex,
2350 length == -1 ? mlirBlockGetNumSuccessors(block.get()) : length,
2351 step),
2352 operation(operation), block(block) {}
2353
2354intptr_t PyBlockSuccessors::getRawNumElements() {
2355 block.checkValid();
2356 return mlirBlockGetNumSuccessors(block.get());
2357}
2358
2359PyBlock PyBlockSuccessors::getRawElement(intptr_t pos) {
2360 MlirBlock block = mlirBlockGetSuccessor(this->block.get(), pos);
2361 return PyBlock(operation, block);
2362}
2363
2365 intptr_t step) const {
2366 return PyBlockSuccessors(block, operation, startIndex, length, step);
2367}
2368
2370 PyOperationRef operation,
2371 intptr_t startIndex, intptr_t length,
2372 intptr_t step)
2373 : Sliceable(startIndex,
2374 length == -1 ? mlirBlockGetNumPredecessors(block.get())
2375 : length,
2376 step),
2377 operation(operation), block(block) {}
2378
2379intptr_t PyBlockPredecessors::getRawNumElements() {
2380 block.checkValid();
2381 return mlirBlockGetNumPredecessors(block.get());
2382}
2383
2384PyBlock PyBlockPredecessors::getRawElement(intptr_t pos) {
2385 MlirBlock block = mlirBlockGetPredecessor(this->block.get(), pos);
2386 return PyBlock(operation, block);
2387}
2388
2389PyBlockPredecessors PyBlockPredecessors::slice(intptr_t startIndex,
2390 intptr_t length,
2391 intptr_t step) const {
2392 return PyBlockPredecessors(block, operation, startIndex, length, step);
2393}
2394
2395nb::typed<nb::object, PyAttribute>
2396PyOpAttributeMap::dunderGetItemNamed(const std::string &name) {
2397 MlirAttribute attr =
2399 if (mlirAttributeIsNull(attr)) {
2400 throw nb::key_error("attempt to access a non-existent attribute");
2402 return PyAttribute(operation->getContext(), attr).maybeDownCast();
2403}
2404
2405nb::typed<nb::object, std::optional<PyAttribute>>
2406PyOpAttributeMap::get(const std::string &key, nb::object defaultValue) {
2407 MlirAttribute attr =
2409 if (mlirAttributeIsNull(attr))
2410 return defaultValue;
2411 return PyAttribute(operation->getContext(), attr).maybeDownCast();
2412}
2413
2415 if (index < 0) {
2416 index += dunderLen();
2417 }
2418 if (index < 0 || index >= dunderLen()) {
2419 throw nb::index_error("attempt to access out of bounds attribute");
2420 }
2421 MlirNamedAttribute namedAttr =
2422 mlirOperationGetAttribute(operation->get(), index);
2423 return PyNamedAttribute(
2424 namedAttr.attribute,
2425 std::string(mlirIdentifierStr(namedAttr.name).data,
2426 mlirIdentifierStr(namedAttr.name).length));
2427}
2428
2429void PyOpAttributeMap::dunderSetItem(const std::string &name,
2430 const PyAttribute &attr) {
2431 mlirOperationSetAttributeByName(operation->get(), toMlirStringRef(name),
2432 attr);
2433}
2434
2435void PyOpAttributeMap::dunderDelItem(const std::string &name) {
2436 int removed = mlirOperationRemoveAttributeByName(operation->get(),
2438 if (!removed)
2439 throw nb::key_error("attempt to delete a non-existent attribute");
2440}
2443 return mlirOperationGetNumAttributes(operation->get());
2444}
2445
2446bool PyOpAttributeMap::dunderContains(const std::string &name) {
2447 return !mlirAttributeIsNull(
2448 mlirOperationGetAttributeByName(operation->get(), toMlirStringRef(name)));
2449}
2450
2452 MlirOperation op, std::function<void(MlirStringRef, MlirAttribute)> fn) {
2454 for (intptr_t i = 0; i < n; ++i) {
2457 fn(name, na.attribute);
2458 }
2459}
2460
2461void PyOpAttributeMap::bind(nb::module_ &m) {
2462 nb::class_<PyOpAttributeMap>(m, "OpAttributeMap")
2463 .def("__contains__", &PyOpAttributeMap::dunderContains, "name"_a,
2464 "Checks if an attribute with the given name exists in the map.")
2465 .def("__len__", &PyOpAttributeMap::dunderLen,
2466 "Returns the number of attributes in the map.")
2467 .def("__getitem__", &PyOpAttributeMap::dunderGetItemNamed, "name"_a,
2468 "Gets an attribute by name.")
2469 .def("__getitem__", &PyOpAttributeMap::dunderGetItemIndexed, "index"_a,
2470 "Gets a named attribute by index.")
2471 .def("__setitem__", &PyOpAttributeMap::dunderSetItem, "name"_a, "attr"_a,
2472 "Sets an attribute with the given name.")
2473 .def("__delitem__", &PyOpAttributeMap::dunderDelItem, "name"_a,
2474 "Deletes an attribute with the given name.")
2475 .def("get", &PyOpAttributeMap::get, nb::arg("key"),
2476 nb::arg("default") = nb::none(),
2477 "Gets an attribute by name or the default value, if it does not "
2478 "exist.")
2479 .def(
2480 "__iter__",
2481 [](PyOpAttributeMap &self) -> nb::typed<nb::iterator, nb::str> {
2482 nb::list keys;
2484 self.operation->get(), [&](MlirStringRef name, MlirAttribute) {
2485 keys.append(nb::str(name.data, name.length));
2486 });
2487 return nb::iter(keys);
2488 },
2489 "Iterates over attribute names.")
2490 .def(
2491 "keys",
2492 [](PyOpAttributeMap &self) -> nb::typed<nb::list, nb::str> {
2493 nb::list out;
2495 self.operation->get(), [&](MlirStringRef name, MlirAttribute) {
2496 out.append(nb::str(name.data, name.length));
2497 });
2498 return out;
2499 },
2500 "Returns a list of attribute names.")
2501 .def(
2502 "values",
2503 [](PyOpAttributeMap &self) -> nb::typed<nb::list, PyAttribute> {
2504 nb::list out;
2506 self.operation->get(), [&](MlirStringRef, MlirAttribute attr) {
2507 out.append(PyAttribute(self.operation->getContext(), attr)
2508 .maybeDownCast());
2509 });
2510 return out;
2511 },
2512 "Returns a list of attribute values.")
2513 .def(
2514 "items",
2515 [](PyOpAttributeMap &self)
2516 -> nb::typed<nb::list,
2517 nb::typed<nb::tuple, nb::str, PyAttribute>> {
2518 nb::list out;
2520 self.operation->get(),
2521 [&](MlirStringRef name, MlirAttribute attr) {
2522 out.append(nb::make_tuple(
2523 nb::str(name.data, name.length),
2524 PyAttribute(self.operation->getContext(), attr)
2525 .maybeDownCast()));
2526 });
2527 return out;
2528 },
2529 "Returns a list of `(name, attribute)` tuples.");
2530}
2531
2532void PyOpAdaptor::bind(nb::module_ &m) {
2533 nb::class_<PyOpAdaptor>(m, "OpAdaptor")
2534 .def(nb::init<nb::typed<nb::list, PyValue>, PyOpAttributeMap>(),
2535 "Creates an OpAdaptor with the given operands and attributes.",
2536 "operands"_a, "attributes"_a)
2537 .def(nb::init<nb::typed<nb::list, PyValue>, PyOpView &>(),
2538 "Creates an OpAdaptor with the given operands and operation view.",
2539 "operands"_a, "opview"_a)
2540 .def_prop_ro(
2541 "operands", [](PyOpAdaptor &self) { return self.operands; },
2542 "Returns the operands of the adaptor.")
2543 .def_prop_ro(
2544 "attributes", [](PyOpAdaptor &self) { return self.attributes; },
2545 "Returns the attributes of the adaptor.");
2546}
2547
2548static MlirLogicalResult verifyTraitByMethod(MlirOperation op, void *userData,
2549 const char *methodName) {
2550 nb::handle targetObj(static_cast<PyObject *>(userData));
2551 if (!nb::hasattr(targetObj, methodName))
2552 return mlirLogicalResultSuccess();
2554 nb::object opView = PyOperation::forOperation(ctx, op)->createOpView();
2555 bool success = nb::cast<bool>(targetObj.attr(methodName)(opView));
2557};
2558
2559static bool attachOpTrait(const nb::object &opName, MlirDynamicOpTrait trait,
2560 PyMlirContext &context) {
2561 std::string opNameStr;
2562 if (opName.is_type()) {
2563 opNameStr = nb::cast<std::string>(opName.attr("OPERATION_NAME"));
2564 } else if (nb::isinstance<nb::str>(opName)) {
2565 opNameStr = nb::cast<std::string>(opName);
2566 } else {
2567 throw nb::type_error("the root argument must be a type or a string");
2568 }
2571 trait, MlirStringRef{opNameStr.data(), opNameStr.size()}, context.get());
2572}
2573
2574bool PyDynamicOpTrait::attach(const nb::object &opName,
2575 const nb::object &target,
2576 PyMlirContext &context) {
2577 if (!nb::hasattr(target, "verify_invariants") &&
2578 !nb::hasattr(target, "verify_region_invariants"))
2579 throw nb::type_error(
2580 "the target object must have at least one of 'verify_invariants' or "
2581 "'verify_region_invariants' methods");
2582
2584 callbacks.construct = [](void *userData) {
2585 nb::handle(static_cast<PyObject *>(userData)).inc_ref();
2586 };
2587 callbacks.destruct = [](void *userData) {
2588 nb::handle(static_cast<PyObject *>(userData)).dec_ref();
2589 };
2590
2591 callbacks.verifyTrait = [](MlirOperation op,
2592 void *userData) -> MlirLogicalResult {
2593 return verifyTraitByMethod(op, userData, "verify_invariants");
2594 };
2595 callbacks.verifyRegionTrait = [](MlirOperation op,
2596 void *userData) -> MlirLogicalResult {
2597 return verifyTraitByMethod(op, userData, "verify_region_invariants");
2598 };
2599
2600 // To ensure that the same dynamic trait gets the same TypeID despite how many
2601 // times `attach` is called, we store it as an attribute on the target class.
2602 if (!nb::hasattr(target, typeIDAttr)) {
2603 nb::setattr(target, typeIDAttr,
2604 nb::cast(PyTypeID(PyGlobals::get().allocateTypeID())));
2605 }
2606 MlirDynamicOpTrait trait = mlirDynamicOpTraitCreate(
2607 nb::cast<PyTypeID>(target.attr(typeIDAttr)).get(), callbacks,
2608 static_cast<void *>(target.ptr()));
2609 return attachOpTrait(opName, trait, context);
2610}
2611
2612void PyDynamicOpTrait::bind(nb::module_ &m) {
2613 nb::class_<PyDynamicOpTrait> cls(m, "DynamicOpTrait");
2614 cls.attr("attach") = classmethod(
2615 [](const nb::object &cls,
2616 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2617 &opName,
2618 nb::object target, DefaultingPyMlirContext context) {
2619 if (target.is_none())
2620 target = cls;
2621 return PyDynamicOpTrait::attach(opName, target, *context.get());
2622 },
2623 nb::arg("cls"), nb::arg("op_name"), nb::arg("target").none() = nb::none(),
2624 nb::arg("context").none() = nb::none(),
2625 "Attach the dynamic op trait subclass to the given operation name.");
2626}
2627
2628bool PyDynamicOpTraits::IsTerminator::attach(const nb::object &opName,
2629 PyMlirContext &context) {
2630 MlirDynamicOpTrait trait = mlirDynamicOpTraitIsTerminatorCreate();
2631 return attachOpTrait(opName, trait, context);
2632}
2633
2634void PyDynamicOpTraits::IsTerminator::bind(nb::module_ &m) {
2635 nb::class_<PyDynamicOpTraits::IsTerminator, PyDynamicOpTrait> cls(
2636 m, "IsTerminatorTrait");
2637 cls.def_prop_ro_static(typeIDAttr, [](nanobind::object & /*class*/) {
2639 });
2640 cls.attr("attach") = classmethod(
2641 [](const nb::object &cls,
2642 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2643 &opName,
2644 DefaultingPyMlirContext context) {
2645 return PyDynamicOpTraits::IsTerminator::attach(opName, *context.get());
2647 "Attach IsTerminator trait to the given operation name.", nb::arg("cls"),
2648 nb::arg("op_name"), nb::arg("context").none() = nb::none());
2649}
2650
2651bool PyDynamicOpTraits::NoTerminator::attach(const nb::object &opName,
2652 PyMlirContext &context) {
2653 MlirDynamicOpTrait trait = mlirDynamicOpTraitNoTerminatorCreate();
2654 return attachOpTrait(opName, trait, context);
2655}
2656
2657void PyDynamicOpTraits::NoTerminator::bind(nb::module_ &m) {
2658 nb::class_<PyDynamicOpTraits::NoTerminator, PyDynamicOpTrait> cls(
2659 m, "NoTerminatorTrait");
2660 cls.def_prop_ro_static(typeIDAttr, [](nanobind::object & /*class*/) {
2662 });
2663 cls.attr("attach") = classmethod(
2664 [](const nb::object &cls,
2665 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2666 &opName,
2667 DefaultingPyMlirContext context) {
2668 return PyDynamicOpTraits::NoTerminator::attach(opName, *context.get());
2670 "Attach NoTerminator trait to the given operation name.", nb::arg("cls"),
2671 nb::arg("op_name"), nb::arg("context").none() = nb::none());
2672}
2673
2674bool PyDynamicOpTraits::IsIsolatedFromAbove::attach(const nb::object &opName,
2675 PyMlirContext &context) {
2676 MlirDynamicOpTrait trait = mlirDynamicOpTraitIsIsolatedFromAboveCreate();
2677 return attachOpTrait(opName, trait, context);
2678}
2679
2681 nb::class_<PyDynamicOpTraits::IsIsolatedFromAbove, PyDynamicOpTrait> cls(
2682 m, "IsIsolatedFromAboveTrait");
2683 cls.def_prop_ro_static(typeIDAttr, [](nanobind::object & /*class*/) {
2685 });
2686 cls.attr("attach") = classmethod(
2687 [](const nb::object &cls,
2688 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2689 &opName,
2690 DefaultingPyMlirContext context) {
2692 *context.get());
2693 },
2694 "Attach IsIsolatedFromAbove trait to the given operation name.",
2695 nb::arg("cls"), nb::arg("op_name"),
2696 nb::arg("context").none() = nb::none());
2697}
2698
2699bool PyDynamicOpTraits::RecursiveMemoryEffects::attach(const nb::object &opName,
2700 PyMlirContext &context) {
2701 MlirDynamicOpTrait trait = mlirDynamicOpTraitRecursiveMemoryEffectsCreate();
2702 return attachOpTrait(opName, trait, context);
2703}
2704
2706 nb::class_<PyDynamicOpTraits::RecursiveMemoryEffects, PyDynamicOpTrait> cls(
2707 m, "RecursiveMemoryEffectsTrait");
2708 cls.def_prop_ro_static(typeIDAttr, [](nanobind::object & /*class*/) {
2710 });
2711 cls.attr("attach") = classmethod(
2712 [](const nb::object &cls,
2713 const nb::typed<nb::object, std::variant<nb::type_object, nb::str>>
2714 &opName,
2715 DefaultingPyMlirContext context) {
2717 opName, *context.get());
2718 },
2719 "Attach RecursiveMemoryEffects trait to the given operation name.",
2720 nb::arg("cls"), nb::arg("op_name"),
2721 nb::arg("context").none() = nb::none());
2722}
2723} // namespace MLIR_BINDINGS_PYTHON_DOMAIN
2724} // namespace python
2725} // namespace mlir
2726
2727namespace {
2728using namespace mlir::python::MLIR_BINDINGS_PYTHON_DOMAIN;
2729
2730MlirLocation tracebackToLocation(MlirContext ctx) {
2731#if defined(Py_LIMITED_API)
2732 // Frame introspection C APIs are not available under the limited API.
2733 // Traceback-based auto-location is not supported; return unknown.
2734 return mlirLocationUnknownGet(ctx);
2735#else
2736 size_t framesLimit =
2738 // Use a thread_local here to avoid requiring a large amount of space.
2739 thread_local std::array<MlirLocation, PyGlobals::TracebackLoc::kMaxFrames>
2740 frames;
2741 size_t count = 0;
2742
2743 nb::gil_scoped_acquire acquire;
2744
2745 PyThreadState *tstate = PyThreadState_GET();
2746 PyFrameObject *next;
2747 PyFrameObject *pyFrame = PyThreadState_GetFrame(tstate);
2748 // In the increment expression:
2749 // 1. get the next prev frame;
2750 // 2. decrement the ref count on the current frame (in order that it can get
2751 // gc'd, along with any objects in its closure and etc);
2752 // 3. set current = next.
2753 for (; pyFrame != nullptr && count < framesLimit;
2754 next = PyFrame_GetBack(pyFrame), Py_XDECREF(pyFrame), pyFrame = next) {
2755 PyCodeObject *code = PyFrame_GetCode(pyFrame);
2756 auto fileNameStr =
2757 nb::cast<std::string>(nb::borrow<nb::str>(code->co_filename));
2758 std::string_view fileName(fileNameStr);
2759 if (!PyGlobals::get().getTracebackLoc().isUserTracebackFilename(fileName))
2760 continue;
2761
2762 // co_qualname and PyCode_Addr2Location added in py3.11
2763#if PY_VERSION_HEX < 0x030B00F0
2764 std::string name =
2765 nb::cast<std::string>(nb::borrow<nb::str>(code->co_name));
2766 std::string_view funcName(name);
2767 int startLine = PyFrame_GetLineNumber(pyFrame);
2768 MlirLocation loc = mlirLocationFileLineColGet(
2769 ctx, mlirStringRefCreate(fileName.data(), fileName.size()), startLine,
2770 0);
2771#else
2772 std::string name =
2773 nb::cast<std::string>(nb::borrow<nb::str>(code->co_qualname));
2774 std::string_view funcName(name);
2775 int startLine, startCol, endLine, endCol;
2776 int lasti = PyFrame_GetLasti(pyFrame);
2777 if (!PyCode_Addr2Location(code, lasti, &startLine, &startCol, &endLine,
2778 &endCol)) {
2779 throw nb::python_error();
2780 }
2781 MlirLocation loc = mlirLocationFileLineColRangeGet(
2782 ctx, mlirStringRefCreate(fileName.data(), fileName.size()), startLine,
2783 startCol, endLine, endCol);
2784#endif
2785
2786 frames[count] = mlirLocationNameGet(
2787 ctx, mlirStringRefCreate(funcName.data(), funcName.size()), loc);
2788 ++count;
2789 }
2790 // When the loop breaks (after the last iter), current frame (if non-null)
2791 // is leaked without this.
2792 Py_XDECREF(pyFrame);
2793
2794 if (count == 0)
2795 return mlirLocationUnknownGet(ctx);
2796
2797 MlirLocation callee = frames[0];
2798 assert(!mlirLocationIsNull(callee) && "expected non-null callee location");
2799 if (count == 1)
2800 return callee;
2801
2802 MlirLocation caller = frames[count - 1];
2803 assert(!mlirLocationIsNull(caller) && "expected non-null caller location");
2804 for (int i = count - 2; i >= 1; i--)
2805 caller = mlirLocationCallSiteGet(frames[i], caller);
2806
2807 return mlirLocationCallSiteGet(callee, caller);
2808#endif
2809}
2810
2811/// Apply currentLocAction: wrap or fuse Location.current onto baseLoc.
2812static MlirLocation
2813applyCurrentLocAction(MlirContext ctx, MlirLocation baseLoc,
2816 if (action == Action::Fallback)
2817 return baseLoc;
2818
2819 auto *currentLoc = PyThreadContextEntry::getDefaultLocation();
2820 if (!currentLoc)
2821 return baseLoc;
2822 assert(mlirLocationGetContext(currentLoc->get()).ptr == ctx.ptr &&
2823 "Location.current must belong to the current MLIR context");
2824
2825 // NamelocWrap: walk the NameLoc chain on Location.current, collect scope
2826 // names, wrap baseLoc innermost-first so result is Outer(Inner(baseLoc)).
2827 // If Location.current is not a NameLoc, scopeNames is empty and baseLoc
2828 // is returned unchanged (nameloc_wrap is a no-op for non-NameLoc contexts).
2829 thread_local std::vector<MlirStringRef> scopeNames;
2830 scopeNames.clear();
2831 MlirLocation walk = currentLoc->get();
2832 while (mlirLocationIsAName(walk)) {
2833 scopeNames.push_back(mlirIdentifierStr(mlirLocationNameGetName(walk)));
2835 }
2836 for (auto it = scopeNames.rbegin(); it != scopeNames.rend(); ++it)
2837 baseLoc = mlirLocationNameGet(ctx, *it, baseLoc);
2838 return baseLoc;
2839}
2840
2841PyLocation
2842maybeGetTracebackLocation(const std::optional<PyLocation> &location) {
2843 auto &tbl = PyGlobals::get().getTracebackLoc();
2844
2845 // Tracebacks not enabled — return explicit loc or fall back to
2846 // Location.current.
2847 if (!tbl.locTracebacksEnabled())
2848 return location.has_value() ? location.value()
2850
2851 // From here: tracebacks are enabled.
2853 PyMlirContext &ctx = DefaultingPyMlirContext::resolve();
2854 MlirLocation baseLoc;
2855
2856 // Step 1: on_explicit — resolve explicit loc= vs traceback.
2857 if (location.has_value()) {
2858 switch (tbl.tracebackActionOnExplicitLoc()) {
2859 case OnExplicit::UseExplicit:
2860 baseLoc = location->get();
2861 break;
2862 case OnExplicit::UseTraceback:
2863 baseLoc = tracebackToLocation(ctx.get());
2864 break;
2865 }
2866 } else {
2867 baseLoc = tracebackToLocation(ctx.get());
2868 }
2869
2870 // Step 2: current_loc — compose with Location.current.
2871 baseLoc = applyCurrentLocAction(ctx.get(), baseLoc,
2872 tbl.tracebackActionOnCurrentLoc());
2873
2875 return {ref, baseLoc};
2876}
2877} // namespace
2878
2879namespace mlir {
2880namespace python {
2882static std::string formatMLIRError(const MLIRError &e) {
2883 auto locStr = [](const PyLocation &loc) {
2884 PyPrintAccumulator accum;
2885 mlirLocationPrint(loc, accum.getCallback(), accum.getUserData());
2886 std::string s = nb::cast<std::string>(nb::str(accum.join()));
2887 std::string_view sv(s);
2888 if (sv.size() > 5) {
2889 sv.remove_prefix(4); // "loc("
2890 sv.remove_suffix(1); // ")"
2891 }
2892 return std::string(sv);
2893 };
2894 auto indent = [](std::string s) {
2895 size_t pos = 0;
2896 while ((pos = s.find('\n', pos)) != std::string::npos) {
2897 s.replace(pos, 1, "\n ");
2898 pos += 3;
2899 }
2900 return s;
2901 };
2902
2903 std::ostringstream os;
2904 os << e.message;
2905 if (!e.errorDiagnostics.empty())
2906 os << ":";
2907 for (const auto &diag : e.errorDiagnostics) {
2908 os << "\nerror: " << locStr(diag.location) << ": " << indent(diag.message);
2909 for (const auto &note : diag.notes) {
2910 os << "\n note: " << locStr(note.location) << ": "
2911 << indent(note.message);
2912 }
2913 }
2914 return os.str();
2915}
2916
2917void MLIRError::bind(nb::module_ &m) {
2918 auto cls = nb::exception<MLIRError>(m, "MLIRError", PyExc_Exception);
2919 nb::register_exception_translator(
2920 [](const std::exception_ptr &p, void *payload) {
2921 try {
2922 if (p)
2923 std::rethrow_exception(p);
2924 } catch (MLIRError &e) {
2925 std::string formatted = formatMLIRError(e);
2926 nb::object ty = nb::borrow(static_cast<PyObject *>(payload));
2927 nb::object obj = ty(formatted);
2928 obj.attr("_message") = nb::cast(std::move(e.message));
2929 obj.attr("_error_diagnostics") =
2930 nb::cast(std::move(e.errorDiagnostics));
2931 PyErr_SetObject(static_cast<PyObject *>(payload), obj.ptr());
2932 }
2933 },
2934 cls.ptr());
2935 auto propertyType = nb::borrow<nb::type_object>(
2936 reinterpret_cast<PyObject *>(&PyProperty_Type));
2937 nb::setattr(
2938 cls, "message",
2939 propertyType(nb::cpp_function(
2940 [](nb::object self) -> nb::str { return self.attr("_message"); },
2941 nb::is_method())));
2942 nb::setattr(cls, "error_diagnostics",
2943 propertyType(nb::cpp_function(
2944 [](nb::object self)
2945 -> nb::typed<nb::list, PyDiagnostic::DiagnosticInfo> {
2946 return self.attr("_error_diagnostics");
2947 },
2948 nb::is_method())));
2949}
2950
2951void populateRoot(nb::module_ &m) {
2952 m.attr("T") = nb::type_var("T");
2953 m.attr("U") = nb::type_var("U");
2954
2955 // Policies for how loc_tracebacks() composes the three location sources
2956 // (explicit loc=, generated traceback, Location.current).
2957 nb::enum_<PyGlobals::TracebackLoc::OnExplicitAction>(m, "OnExplicitAction")
2958 .value("USE_EXPLICIT",
2960 .value("USE_TRACEBACK",
2962
2963 nb::enum_<PyGlobals::TracebackLoc::CurrentLocAction>(m, "CurrentLocAction")
2965 .value("NAMELOC_WRAP",
2967
2968 nb::class_<PyGlobals>(m, "_Globals")
2969 .def_prop_rw("dialect_search_modules",
2972 .def("append_dialect_search_prefix", &PyGlobals::addDialectSearchPrefix,
2973 "module_name"_a)
2974 .def(
2975 "_check_dialect_module_loaded",
2976 [](PyGlobals &self, const std::string &dialectNamespace) {
2977 return self.loadDialectModule(dialectNamespace);
2978 },
2979 "dialect_namespace"_a)
2980 .def("_register_dialect_impl", &PyGlobals::registerDialectImpl,
2981 "dialect_namespace"_a, "dialect_class"_a, nb::kw_only(),
2982 "replace"_a = false,
2983 "Testing hook for directly registering a dialect")
2984 .def("_register_operation_impl", &PyGlobals::registerOperationImpl,
2985 "operation_name"_a, "operation_class"_a, nb::kw_only(),
2986 "replace"_a = false,
2987 "Testing hook for directly registering an operation")
2988 .def("loc_tracebacks_enabled",
2989 [](PyGlobals &self) {
2990 return self.getTracebackLoc().locTracebacksEnabled();
2991 })
2992 .def("set_loc_tracebacks_enabled",
2993 [](PyGlobals &self, bool enabled) {
2995 })
2996 .def("loc_tracebacks_frame_limit",
2997 [](PyGlobals &self) {
2999 })
3000 .def("set_loc_tracebacks_frame_limit",
3001 [](PyGlobals &self, std::optional<int> n) {
3004 })
3005 .def("register_traceback_file_inclusion",
3006 [](PyGlobals &self, const std::string &filename) {
3008 })
3009 .def("register_traceback_file_exclusion",
3010 [](PyGlobals &self, const std::string &filename) {
3012 })
3013 .def("traceback_action_on_explicit_loc",
3014 [](PyGlobals &self) {
3016 })
3017 .def("set_traceback_action_on_explicit_loc",
3018 [](PyGlobals &self,
3021 })
3022 .def("traceback_action_on_current_loc",
3023 [](PyGlobals &self) {
3025 })
3026 .def("set_traceback_action_on_current_loc",
3027 [](PyGlobals &self,
3030 });
3031
3032 // Aside from making the globals accessible to python, having python manage
3033 // it is necessary to make sure it is destroyed (and releases its python
3034 // resources) properly.
3035 m.attr("globals") = nb::cast(new PyGlobals, nb::rv_policy::take_ownership);
3036
3037 // Registration decorators.
3038 m.def(
3039 "register_dialect",
3040 [](nb::type_object pyClass) {
3041 std::string dialectNamespace =
3042 nb::cast<std::string>(pyClass.attr("DIALECT_NAMESPACE"));
3043 PyGlobals::get().registerDialectImpl(dialectNamespace, pyClass);
3044 return pyClass;
3045 },
3046 "dialect_class"_a,
3047 "Class decorator for registering a custom Dialect wrapper");
3048 m.def(
3049 "register_operation",
3050 [](const nb::type_object &dialectClass, bool replace) -> nb::object {
3051 return nb::cpp_function(
3052 [dialectClass,
3053 replace](nb::type_object opClass) -> nb::type_object {
3054 std::string operationName =
3055 nb::cast<std::string>(opClass.attr("OPERATION_NAME"));
3056 PyGlobals::get().registerOperationImpl(operationName, opClass,
3057 replace);
3058 // Dict-stuff the new opClass by name onto the dialect class.
3059 nb::object opClassName = opClass.attr("__name__");
3060 dialectClass.attr(opClassName) = opClass;
3061 return opClass;
3062 });
3063 },
3064 // clang-format off
3065 nb::sig("def register_operation(dialect_class: type, *, replace: bool = False) "
3066 "-> typing.Callable[[type[T]], type[T]]"),
3067 // clang-format on
3068 "dialect_class"_a, nb::kw_only(), "replace"_a = false,
3069 "Produce a class decorator for registering an Operation class as part of "
3070 "a dialect");
3071 m.def(
3072 "register_op_adaptor",
3073 [](const nb::type_object &opClass, bool replace) -> nb::object {
3074 return nb::cpp_function(
3075 [opClass,
3076 replace](nb::type_object adaptorClass) -> nb::type_object {
3077 std::string operationName =
3078 nb::cast<std::string>(adaptorClass.attr("OPERATION_NAME"));
3079 PyGlobals::get().registerOpAdaptorImpl(operationName,
3080 adaptorClass, replace);
3081 // Dict-stuff the new adaptorClass by name onto the opClass.
3082 opClass.attr("Adaptor") = adaptorClass;
3083 return adaptorClass;
3084 });
3085 },
3086 // clang-format off
3087 nb::sig("def register_op_adaptor(op_class: type, *, replace: bool = False) "
3088 "-> typing.Callable[[type[T]], type[T]]"),
3089 // clang-format on
3090 "op_class"_a, nb::kw_only(), "replace"_a = false,
3091 "Produce a class decorator for registering an OpAdaptor class for an "
3092 "operation.");
3093 m.def(
3095 [](PyTypeID mlirTypeID, bool replace) -> nb::object {
3096 return nb::cpp_function([mlirTypeID, replace](
3097 nb::callable typeCaster) -> nb::object {
3098 PyGlobals::get().registerTypeCaster(mlirTypeID, typeCaster, replace);
3099 return typeCaster;
3100 });
3101 },
3102 // clang-format off
3103 nb::sig("def register_type_caster(typeid: _mlir.ir.TypeID, *, replace: bool = False) "
3104 "-> typing.Callable[[typing.Callable[[T], U]], typing.Callable[[T], U]]"),
3105 // clang-format on
3106 "typeid"_a, nb::kw_only(), "replace"_a = false,
3107 "Register a type caster for casting MLIR types to custom user types.");
3108 m.def(
3110 [](PyTypeID mlirTypeID, bool replace) -> nb::object {
3111 return nb::cpp_function(
3112 [mlirTypeID, replace](nb::callable valueCaster) -> nb::object {
3113 PyGlobals::get().registerValueCaster(mlirTypeID, valueCaster,
3114 replace);
3115 return valueCaster;
3116 });
3117 },
3118 // clang-format off
3119 nb::sig("def register_value_caster(typeid: _mlir.ir.TypeID, *, replace: bool = False) "
3120 "-> typing.Callable[[typing.Callable[[T], U]], typing.Callable[[T], U]]"),
3121 // clang-format on
3122 "typeid"_a, nb::kw_only(), "replace"_a = false,
3123 "Register a value caster for casting MLIR values to custom user values.");
3124}
3125
3126//------------------------------------------------------------------------------
3127// Location subclass bindDerived implementations.
3128//------------------------------------------------------------------------------
3129
3131 c.def_static(
3132 "get",
3133 [](DefaultingPyMlirContext context) {
3134 return PyUnknownLocation(context->getRef(),
3135 mlirLocationUnknownGet(context->get()));
3136 },
3137 "context"_a = nb::none(),
3138 "Gets a Location representing an unknown location.");
3139}
3140
3142 c.def_static(
3143 "get",
3144 [](std::string filename, int line, int col,
3145 DefaultingPyMlirContext context) {
3146 return PyFileLineColLocation(
3147 context->getRef(),
3149 toMlirStringRef(filename), line, col));
3150 },
3151 "filename"_a, "line"_a, "col"_a, "context"_a = nb::none(),
3152 "Gets a FileLineColLoc for a file, line, and column.");
3153 c.def_static(
3154 "get",
3155 [](std::string filename, int startLine, int startCol, int endLine,
3156 int endCol, DefaultingPyMlirContext context) {
3157 return PyFileLineColLocation(
3158 context->getRef(), mlirLocationFileLineColRangeGet(
3159 context->get(), toMlirStringRef(filename),
3160 startLine, startCol, endLine, endCol));
3161 },
3162 "filename"_a, "start_line"_a, "start_col"_a, "end_line"_a, "end_col"_a,
3163 "context"_a = nb::none(),
3164 "Gets a FileLineColLoc spanning a file and line/column range.");
3165 c.def_prop_ro(
3166 "filename",
3167 [](PyFileLineColLocation &self) {
3168 return mlirIdentifierStr(
3170 },
3171 "Gets the filename from a `FileLineColLoc`.");
3172 c.def_prop_ro(
3173 "start_line",
3174 [](PyFileLineColLocation &self) {
3176 },
3177 "Gets the start line number from a `FileLineColLoc`.");
3178 c.def_prop_ro(
3179 "start_col",
3180 [](PyFileLineColLocation &self) {
3182 },
3183 "Gets the start column number from a `FileLineColLoc`.");
3184 c.def_prop_ro(
3185 "end_line",
3186 [](PyFileLineColLocation &self) {
3188 },
3189 "Gets the end line number from a `FileLineColLoc`.");
3190 c.def_prop_ro(
3191 "end_col",
3192 [](PyFileLineColLocation &self) {
3194 },
3195 "Gets the end column number from a `FileLineColLoc`.");
3196}
3197
3199 c.def_static(
3200 "get",
3201 [](std::string name, std::optional<PyLocation> childLoc,
3202 DefaultingPyMlirContext context) {
3203 return PyNameLocation(
3204 context->getRef(),
3205 mlirLocationNameGet(context->get(), toMlirStringRef(name),
3206 childLoc
3207 ? childLoc->get()
3208 : mlirLocationUnknownGet(context->get())));
3209 },
3210 "name"_a, "child_loc"_a = nb::none(), "context"_a = nb::none(),
3211 "Gets a NameLoc with an optional child location.");
3212 c.def_prop_ro(
3213 "name_str",
3214 [](PyNameLocation &self) {
3216 },
3217 "Gets the name string from a `NameLoc`.");
3218 c.def_prop_ro(
3219 "child_loc",
3220 [](PyNameLocation &self) {
3221 return PyLocation(self.getContext(),
3223 .maybeDownCast();
3224 },
3225 "Gets the child location from a `NameLoc`.");
3226}
3227
3229 c.def_static(
3230 "get",
3231 [](PyLocation callee, const std::vector<PyLocation> &frames,
3232 DefaultingPyMlirContext context) {
3233 if (frames.empty())
3234 throw nb::value_error("No caller frames provided.");
3235 MlirLocation caller = frames.back().get();
3236 for (size_t index = frames.size() - 1; index-- > 0;) {
3237 caller = mlirLocationCallSiteGet(frames[index].get(), caller);
3238 }
3239 return PyCallSiteLocation(
3240 context->getRef(), mlirLocationCallSiteGet(callee.get(), caller));
3241 },
3242 "callee"_a, "frames"_a, "context"_a = nb::none(),
3243 "Gets a CallSiteLoc chaining a callee and one or more caller frames.");
3244 c.def_prop_ro(
3245 "callee",
3246 [](PyCallSiteLocation &self) {
3247 return PyLocation(self.getContext(),
3249 .maybeDownCast();
3250 },
3251 "Gets the callee location from a `CallSiteLoc`.");
3252 c.def_prop_ro(
3253 "caller",
3254 [](PyCallSiteLocation &self) {
3255 return PyLocation(self.getContext(),
3257 .maybeDownCast();
3258 },
3259 "Gets the caller location from a `CallSiteLoc`.");
3260}
3261
3263 c.def_static(
3264 "get",
3265 [](const std::vector<PyLocation> &pyLocations,
3266 std::optional<PyAttribute> metadata, DefaultingPyMlirContext context) {
3267 std::vector<MlirLocation> locations;
3268 locations.reserve(pyLocations.size());
3269 for (const PyLocation &pyLocation : pyLocations)
3270 locations.push_back(pyLocation.get());
3271 MlirLocation location = mlirLocationFusedGet(
3272 context->get(), locations.size(), locations.data(),
3273 metadata ? metadata->get() : MlirAttribute{0});
3274 // Strict: `Location.fused(...)` handles the collapse case.
3275 if (!mlirLocationIsAFused(location))
3276 throw nb::value_error(
3277 "FusedLoc.get would collapse to a non-fused location; use "
3278 "Location.fused(...) for the permissive variant.");
3279 return PyFusedLocation(context->getRef(), location);
3280 },
3281 "locations"_a, "metadata"_a = nb::none(), "context"_a = nb::none(),
3282 "Gets a FusedLoc from an array of locations and optional metadata. "
3283 "Raises if the fuse would collapse to a non-fused location; use "
3284 "`Location.fused(...)` for the permissive variant.");
3285 c.def_prop_ro(
3286 "locations",
3287 [](PyFusedLocation &self) {
3288 unsigned numLocations = mlirLocationFusedGetNumLocations(self.get());
3289 std::vector<MlirLocation> locations(numLocations);
3290 if (numLocations)
3291 mlirLocationFusedGetLocations(self.get(), locations.data());
3292 std::vector<nb::object> pyLocations;
3293 pyLocations.reserve(numLocations);
3294 for (unsigned i = 0; i < numLocations; ++i)
3295 pyLocations.push_back(
3296 PyLocation(self.getContext(), locations[i]).maybeDownCast());
3297 return pyLocations;
3298 },
3299 "Gets the list of locations from a `FusedLoc`.");
3300 c.def_prop_ro(
3301 "metadata",
3302 [](PyFusedLocation &self) -> std::optional<PyAttribute> {
3303 MlirAttribute metadata = mlirLocationFusedGetMetadata(self.get());
3304 if (mlirAttributeIsNull(metadata))
3305 return std::nullopt;
3306 return PyAttribute(self.getContext(), metadata);
3307 },
3308 "Gets the metadata attribute from a `FusedLoc`, or None if absent.");
3309}
3310
3311//------------------------------------------------------------------------------
3312// Populates the core exports of the 'ir' submodule.
3313//------------------------------------------------------------------------------
3314void populateIRCore(nb::module_ &m) {
3315 //----------------------------------------------------------------------------
3316 // Enums.
3317 //----------------------------------------------------------------------------
3318 nb::enum_<PyDiagnosticSeverity>(m, "DiagnosticSeverity")
3319 .value("ERROR", PyDiagnosticSeverity::Error)
3320 .value("WARNING", PyDiagnosticSeverity::Warning)
3321 .value("NOTE", PyDiagnosticSeverity::Note)
3322 .value("REMARK", PyDiagnosticSeverity::Remark);
3323
3324 nb::enum_<PyWalkOrder>(m, "WalkOrder")
3325 .value("PRE_ORDER", PyWalkOrder::PreOrder)
3326 .value("POST_ORDER", PyWalkOrder::PostOrder);
3327
3328 nb::enum_<PyOperationEquivalenceFlags>(m, "OperationEquivalenceFlags",
3329 nb::is_arithmetic(), nb::is_flag())
3330 .value("NONE", PyOperationEquivalenceFlags::None)
3331 .value("IGNORE_LOCATIONS", PyOperationEquivalenceFlags::IgnoreLocations)
3332 .value("IGNORE_DISCARDABLE_ATTRS",
3334 .value("IGNORE_PROPERTIES", PyOperationEquivalenceFlags::IgnoreProperties)
3335 .value("IGNORE_COMMUTATIVITY",
3337
3338 nb::enum_<PyWalkResult>(m, "WalkResult")
3339 .value("ADVANCE", PyWalkResult::Advance)
3340 .value("INTERRUPT", PyWalkResult::Interrupt)
3341 .value("SKIP", PyWalkResult::Skip);
3342
3343 //----------------------------------------------------------------------------
3344 // Mapping of Diagnostics.
3345 //----------------------------------------------------------------------------
3346 nb::class_<PyDiagnostic>(m, "Diagnostic")
3347 .def_prop_ro("severity", &PyDiagnostic::getSeverity,
3348 "Returns the severity of the diagnostic.")
3349 .def_prop_ro("location", &PyDiagnostic::getLocation,
3350 "Returns the location associated with the diagnostic.")
3351 .def_prop_ro("message", &PyDiagnostic::getMessage,
3352 "Returns the message text of the diagnostic.")
3353 .def_prop_ro("notes", &PyDiagnostic::getNotes,
3354 "Returns a tuple of attached note diagnostics.")
3355 .def(
3356 "__str__",
3357 [](PyDiagnostic &self) -> nb::str {
3358 if (!self.isValid())
3359 return nb::str("<Invalid Diagnostic>");
3360 return self.getMessage();
3361 },
3362 "Returns the diagnostic message as a string.");
3363
3364 nb::class_<PyDiagnostic::DiagnosticInfo>(m, "DiagnosticInfo")
3365 .def(
3366 "__init__",
3368 new (&self) PyDiagnostic::DiagnosticInfo(diag.getInfo());
3369 },
3370 "diag"_a, "Creates a DiagnosticInfo from a Diagnostic.")
3371 .def_ro("severity", &PyDiagnostic::DiagnosticInfo::severity,
3372 "The severity level of the diagnostic.")
3373 .def_ro("location", &PyDiagnostic::DiagnosticInfo::location,
3374 "The location associated with the diagnostic.")
3375 .def_ro("message", &PyDiagnostic::DiagnosticInfo::message,
3376 "The message text of the diagnostic.")
3377 .def_ro("notes", &PyDiagnostic::DiagnosticInfo::notes,
3378 "List of attached note diagnostics.")
3379 .def(
3380 "__str__",
3381 [](PyDiagnostic::DiagnosticInfo &self) { return self.message; },
3382 "Returns the diagnostic message as a string.");
3383
3384 nb::class_<PyDiagnosticHandler>(m, "DiagnosticHandler")
3385 .def("detach", &PyDiagnosticHandler::detach,
3386 "Detaches the diagnostic handler from the context.")
3387 .def_prop_ro("attached", &PyDiagnosticHandler::isAttached,
3388 "Returns True if the handler is attached to a context.")
3389 .def_prop_ro("had_error", &PyDiagnosticHandler::getHadError,
3390 "Returns True if an error was encountered during diagnostic "
3391 "handling.")
3392 .def("__enter__", &PyDiagnosticHandler::contextEnter,
3393 "Enters the diagnostic handler as a context manager.",
3394 nb::sig("def __enter__(self, /) -> DiagnosticHandler"))
3395 .def("__exit__", &PyDiagnosticHandler::contextExit, "exc_type"_a.none(),
3396 "exc_value"_a.none(), "traceback"_a.none(),
3397 "Exits the diagnostic handler context manager.");
3398
3399 // Expose DefaultThreadPool to python
3400 nb::class_<PyThreadPool>(m, "ThreadPool")
3401 .def(
3402 "__init__", [](PyThreadPool &self) { new (&self) PyThreadPool(); },
3403 "Creates a new thread pool with default concurrency.")
3404 .def("get_max_concurrency", &PyThreadPool::getMaxConcurrency,
3405 "Returns the maximum number of threads in the pool.")
3406 .def("_mlir_thread_pool_ptr", &PyThreadPool::_mlir_thread_pool_ptr,
3407 "Returns the raw pointer to the LLVM thread pool as a string.");
3408
3409 nb::class_<PyMlirContext>(m, "Context")
3410 .def(
3411 "__init__",
3412 [](PyMlirContext &self) {
3413 MlirContext context = mlirContextCreateWithThreading(false);
3414 new (&self) PyMlirContext(context);
3415 },
3416 R"(
3417 Creates a new MLIR context.
3418
3419 The context is the top-level container for all MLIR objects. It owns the storage
3420 for types, attributes, locations, and other core IR objects. A context can be
3421 configured to allow or disallow unregistered dialects and can have dialects
3422 loaded on-demand.)")
3423 .def_static("_get_live_count", &PyMlirContext::getLiveCount,
3424 "Gets the number of live Context objects.")
3425 .def(
3426 "_get_context_again",
3427 [](PyMlirContext &self) -> nb::typed<nb::object, PyMlirContext> {
3429 return ref.releaseObject();
3430 },
3431 "Gets another reference to the same context.")
3432 .def("_get_live_module_count", &PyMlirContext::getLiveModuleCount,
3433 "Gets the number of live modules owned by this context.")
3435 "Gets a capsule wrapping the MlirContext.")
3438 "Creates a Context from a capsule wrapping MlirContext.")
3439 .def("__enter__", &PyMlirContext::contextEnter,
3440 "Enters the context as a context manager.",
3441 nb::sig("def __enter__(self, /) -> Context"))
3442 .def("__exit__", &PyMlirContext::contextExit, "exc_type"_a.none(),
3443 "exc_value"_a.none(), "traceback"_a.none(),
3444 "Exits the context manager.")
3445 .def_prop_ro_static(
3446 "current",
3447 [](nb::object & /*class*/)
3448 -> std::optional<nb::typed<nb::object, PyMlirContext>> {
3450 if (!context)
3451 return {};
3452 return nb::cast(context);
3453 },
3454 nb::sig("def current(/) -> Context | None"),
3455 "Gets the Context bound to the current thread or returns None if no "
3456 "context is set.")
3457 .def_prop_ro(
3458 "dialects",
3459 [](PyMlirContext &self) { return PyDialects(self.getRef()); },
3460 "Gets a container for accessing dialects by name.")
3461 .def_prop_ro(
3462 "d", [](PyMlirContext &self) { return PyDialects(self.getRef()); },
3463 "Alias for `dialects`.")
3464 .def(
3465 "get_dialect_descriptor",
3466 [](PyMlirContext &self, std::string &name) {
3467 MlirDialect dialect = mlirContextGetOrLoadDialect(
3468 self.get(), {name.data(), name.size()});
3469 if (mlirDialectIsNull(dialect)) {
3470 throw nb::value_error(
3471 join("Dialect '", name, "' not found").c_str());
3472 }
3473 return PyDialectDescriptor(self.getRef(), dialect);
3474 },
3475 "dialect_name"_a,
3476 "Gets or loads a dialect by name, returning its descriptor object.")
3477 .def(
3478 "is_dialect_loaded",
3479 [](PyMlirContext &self, std::string &name) {
3480 MlirDialect dialect = mlirContextGetLoadedDialect(
3481 self.get(), {name.data(), name.size()});
3482 return !mlirDialectIsNull(dialect);
3483 },
3484 "dialect_name"_a, "Checks if a dialect is loaded in the context.")
3485 .def_prop_rw(
3486 "allow_unregistered_dialects",
3487 [](PyMlirContext &self) -> bool {
3488 return mlirContextGetAllowUnregisteredDialects(self.get());
3489 },
3490 [](PyMlirContext &self, bool value) {
3491 mlirContextSetAllowUnregisteredDialects(self.get(), value);
3492 },
3493 "Controls whether unregistered dialects are allowed in this context.")
3494 .def("attach_diagnostic_handler", &PyMlirContext::attachDiagnosticHandler,
3495 "callback"_a,
3496 "Attaches a diagnostic handler that will receive callbacks.")
3497 .def("enable_remarks", &PyMlirContext::enableRemarks, nb::kw_only(),
3498 "policy"_a = PyRemarkPolicy::All,
3499 "output_file"_a.none() = nb::none(),
3500 "format"_a = PyRemarkFormat::YAML, "all_filter"_a = "",
3501 "passed_filter"_a = "", "missed_filter"_a = "",
3502 "analysis_filter"_a = "", "failed_filter"_a = "",
3503 "callback"_a.none() = nb::none(),
3504 "print_as_emit_remarks"_a.none() = nb::none(),
3505 R"(
3506 Enables the optimization remark engine on this context.
3507
3508 Remarks are reported by passes (and by `Location.emit_remark`) and
3509 selected by the category filters, regular expressions anchored by
3510 the engine: `all_filter` applies to every kind, the other filters to
3511 one kind each; a kind without a matching filter is not reported.
3512
3513 The sink is selected by the arguments: `output_file` streams the
3514 remarks to a file in `format`, `callback` delivers them to Python,
3515 and without either they are emitted as MLIR remark diagnostics.
3516
3517 Args:
3518 policy: `RemarkPolicy.ALL` reports remarks as they are emitted;
3519 `RemarkPolicy.FINAL` postpones them until `finalize_remarks()`
3520 and reports the final set, grouping related remarks under
3521 their parents.
3522 output_file: Path of the remark file, written on
3523 `finalize_remarks()`.
3524 format: The serialization format of `output_file`.
3525 all_filter: Category regex applied to every remark kind.
3526 passed_filter: Category regex for passed remarks.
3527 missed_filter: Category regex for missed remarks.
3528 analysis_filter: Category regex for analysis remarks.
3529 failed_filter: Category regex for failed remarks.
3530 callback: A callable receiving one `Remark` per reported remark;
3531 the remark is only valid during the call.
3532 print_as_emit_remarks: Also emit every remark as an MLIR remark
3533 diagnostic; defaults to True only without an output file or
3534 callback.
3535
3536 Raises:
3537 ValueError: When both an output file and a callback are given,
3538 when remarks are already enabled, or when the output file
3539 cannot be written.)")
3540 .def("finalize_remarks", &PyMlirContext::finalizeRemarks,
3541 R"(
3542 Finalizes and removes the remark engine of this context.
3543
3544 Postponed remarks (policy "final") are reported, the output file is
3545 written and the callback is released. Does nothing when no engine
3546 is enabled; `enable_remarks` may be called again afterwards.)")
3547 .def_prop_ro(
3548 "remarks_enabled", &PyMlirContext::getRemarksEnabled,
3549 "Returns True if a remark engine is enabled on this context.")
3550 .def(
3551 "enable_multithreading",
3552 [](PyMlirContext &self, bool enable) {
3553 mlirContextEnableMultithreading(self.get(), enable);
3554 },
3555 "enable"_a,
3556 R"(
3557 Enables or disables multi-threading support in the context.
3558
3559 Args:
3560 enable: Whether to enable (True) or disable (False) multi-threading.
3561 )")
3562 .def(
3563 "set_thread_pool",
3564 [](PyMlirContext &self, PyThreadPool &pool) {
3565 // we should disable multi-threading first before setting
3566 // new thread pool otherwise the assert in
3567 // MLIRContext::setThreadPool will be raised.
3568 mlirContextEnableMultithreading(self.get(), false);
3569 mlirContextSetThreadPool(self.get(), pool.get());
3570 },
3571 R"(
3572 Sets a custom thread pool for the context to use.
3573
3574 Args:
3575 pool: A ThreadPool object to use for parallel operations.
3576
3577 Note:
3578 Multi-threading is automatically disabled before setting the thread pool.)")
3579 .def(
3580 "get_num_threads",
3581 [](PyMlirContext &self) {
3582 return mlirContextGetNumThreads(self.get());
3583 },
3584 "Gets the number of threads in the context's thread pool.")
3585 .def(
3586 "_mlir_thread_pool_ptr",
3587 [](PyMlirContext &self) {
3588 MlirLlvmThreadPool pool = mlirContextGetThreadPool(self.get());
3589 std::stringstream ss;
3590 ss << pool.ptr;
3591 return ss.str();
3592 },
3593 "Gets the raw pointer to the LLVM thread pool as a string.")
3594 .def(
3595 "is_registered_operation",
3596 [](PyMlirContext &self, std::string &name) {
3598 self.get(), MlirStringRef{name.data(), name.size()});
3599 },
3600 "operation_name"_a,
3601 R"(
3602 Checks whether an operation with the given name is registered.
3603
3604 Args:
3605 operation_name: The fully qualified name of the operation (e.g., `arith.addf`).
3606
3607 Returns:
3608 True if the operation is registered, False otherwise.)")
3609 .def(
3610 "append_dialect_registry",
3611 [](PyMlirContext &self, PyDialectRegistry &registry) {
3612 mlirContextAppendDialectRegistry(self.get(), registry);
3613 },
3614 "registry"_a,
3615 R"(
3616 Appends the contents of a dialect registry to the context.
3617
3618 Args:
3619 registry: A DialectRegistry containing dialects to append.)")
3620 .def_prop_rw("emit_error_diagnostics",
3623 R"(
3624 Controls whether error diagnostics are emitted to diagnostic handlers.
3625
3626 By default, error diagnostics are captured and reported through MLIRError exceptions.)")
3627 .def(
3628 "load_all_available_dialects",
3629 [](PyMlirContext &self) {
3631 },
3632 R"(
3633 Loads all dialects available in the registry into the context.
3634
3635 This eagerly loads all dialects that have been registered, making them
3636 immediately available for use.)")
3637 .def(
3638 "begin_transient_scope",
3639 [](PyMlirContext &self) {
3640 if (mlirContextIsInTransientScope(self.get()))
3641 throw nb::value_error("Context is already in a transient scope");
3643 },
3644 R"(
3645 Begins a transient scope on the context, freezing the base layer.
3646
3647 All subsequently allocated types, attributes, and unregistered operations
3648 are treated as transient and will be deallocated with end_transient_scope().
3649 Raises a ValueError if the context is already in a transient scope.)")
3650 .def(
3651 "end_transient_scope",
3652 [](PyMlirContext &self) { mlirContextEndTransientScope(self.get()); },
3653 R"(
3654 Ends the transient scope and resets the context to the base state.
3655
3656 Prunes all transient types, attributes, affine expressions, distinct
3657 attributes, and unregistered operations added during the transient scope.
3658
3659 Note: Any Python objects referencing transient IR entities become invalid
3660 after this call and must not be accessed.)")
3661 .def_prop_ro(
3662 "is_in_transient_scope",
3663 [](PyMlirContext &self) -> bool {
3664 return mlirContextIsInTransientScope(self.get());
3665 },
3666 "Returns whether the context is currently in a transient scope.");
3667
3668 //----------------------------------------------------------------------------
3669 // Mapping of PyDialectDescriptor
3670 //----------------------------------------------------------------------------
3671 nb::class_<PyDialectDescriptor>(m, "DialectDescriptor")
3672 .def_prop_ro(
3673 "namespace",
3674 [](PyDialectDescriptor &self) {
3675 MlirStringRef ns = mlirDialectGetNamespace(self.get());
3676 return nb::str(ns.data, ns.length);
3677 },
3678 "Returns the namespace of the dialect.")
3679 .def(
3680 "__repr__",
3681 [](PyDialectDescriptor &self) {
3682 MlirStringRef ns = mlirDialectGetNamespace(self.get());
3683 std::string repr("<DialectDescriptor ");
3684 repr.append(ns.data, ns.length);
3685 repr.append(">");
3686 return repr;
3687 },
3688 nb::sig("def __repr__(self) -> str"),
3689 "Returns a string representation of the dialect descriptor.");
3690
3691 //----------------------------------------------------------------------------
3692 // Mapping of PyDialects
3693 //----------------------------------------------------------------------------
3694 nb::class_<PyDialects>(m, "Dialects")
3695 .def(
3696 "__getitem__",
3697 [](PyDialects &self, std::string keyName) {
3698 MlirDialect dialect =
3699 self.getDialectForKey(keyName, /*attrError=*/false);
3700 nb::object descriptor =
3701 nb::cast(PyDialectDescriptor{self.getContext(), dialect});
3702 return createCustomDialectWrapper(keyName, std::move(descriptor));
3703 },
3704 "Gets a dialect by name using subscript notation.")
3705 .def(
3706 "__getattr__",
3707 [](PyDialects &self, std::string attrName) {
3708 MlirDialect dialect =
3709 self.getDialectForKey(attrName, /*attrError=*/true);
3710 nb::object descriptor =
3711 nb::cast(PyDialectDescriptor{self.getContext(), dialect});
3712 return createCustomDialectWrapper(attrName, std::move(descriptor));
3713 },
3714 "Gets a dialect by name using attribute notation.");
3715
3716 //----------------------------------------------------------------------------
3717 // Mapping of PyDialect
3718 //----------------------------------------------------------------------------
3719 nb::class_<PyDialect>(m, "Dialect")
3720 .def(nb::init<nb::object>(), "descriptor"_a,
3721 "Creates a Dialect from a DialectDescriptor.")
3722 .def_prop_ro(
3723 "descriptor", [](PyDialect &self) { return self.getDescriptor(); },
3724 "Returns the DialectDescriptor for this dialect.")
3725 .def(
3726 "__repr__",
3727 [](const nb::object &self) {
3728 auto clazz = self.attr("__class__");
3729 return nb::str("<Dialect ") +
3730 self.attr("descriptor").attr("namespace") +
3731 nb::str(" (class ") + clazz.attr("__module__") +
3732 nb::str(".") + clazz.attr("__name__") + nb::str(")>");
3733 },
3734 nb::sig("def __repr__(self) -> str"),
3735 "Returns a string representation of the dialect.");
3736
3737 //----------------------------------------------------------------------------
3738 // Mapping of PyDialectRegistry
3739 //----------------------------------------------------------------------------
3740 nb::class_<PyDialectRegistry>(m, "DialectRegistry")
3742 "Gets a capsule wrapping the MlirDialectRegistry.")
3745 "Creates a DialectRegistry from a capsule wrapping "
3746 "`MlirDialectRegistry`.")
3747 .def(nb::init<>(), "Creates a new empty dialect registry.");
3748
3749 //----------------------------------------------------------------------------
3750 // Mapping of Location
3751 //----------------------------------------------------------------------------
3752 nb::class_<PyLocation>(m, "Location")
3754 "Gets a capsule wrapping the MlirLocation.")
3756 "Creates a Location from a capsule wrapping MlirLocation.")
3757 .def("__enter__", &PyLocation::contextEnter,
3758 "Enters the location as a context manager.",
3759 nb::sig("def __enter__(self, /) -> Location"))
3760 .def("__exit__", &PyLocation::contextExit, "exc_type"_a.none(),
3761 "exc_value"_a.none(), "traceback"_a.none(),
3762 "Exits the location context manager.")
3763 .def(
3764 "__eq__",
3765 [](PyLocation &self, PyLocation &other) -> bool {
3766 return mlirLocationEqual(self, other);
3767 },
3768 "Compares two locations for equality.")
3769 .def(
3770 "__eq__", [](PyLocation &self, nb::object other) { return false; },
3771 "Compares location with non-location object (always returns False).")
3772 .def_prop_ro_static(
3773 "current",
3774 [](nb::object & /*class*/) -> std::optional<PyLocation *> {
3776 if (!loc)
3777 return std::nullopt;
3778 return loc;
3779 },
3780 // clang-format off
3781 nb::sig("def current(/) -> Location | None"),
3782 // clang-format on
3783 "Gets the Location bound to the current thread or raises ValueError.")
3784 .def_static(
3785 "from_attr",
3786 [](PyAttribute &attribute, DefaultingPyMlirContext context) {
3787 return PyLocation(context->getRef(),
3788 mlirLocationFromAttribute(attribute))
3789 .maybeDownCast();
3790 },
3791 "attribute"_a, "context"_a = nb::none(),
3792 "Gets a Location from a `LocationAttr`.")
3793 // Factory shims kept for backward compatibility; return the concrete
3794 // subclass. New code should use the subclass `.get()` directly.
3795 .def_static(
3796 "unknown",
3797 [](DefaultingPyMlirContext context) {
3798 return PyUnknownLocation(context->getRef(),
3799 mlirLocationUnknownGet(context->get()));
3800 },
3801 "context"_a = nb::none(), "Alias for `UnknownLoc.get()`.")
3802 .def_static(
3803 "file",
3804 [](std::string filename, int line, int col,
3805 DefaultingPyMlirContext context) {
3806 return PyFileLineColLocation(
3807 context->getRef(),
3809 context->get(), toMlirStringRef(filename), line, col));
3810 },
3811 "filename"_a, "line"_a, "col"_a, "context"_a = nb::none(),
3812 "Alias for `FileLineColLoc.get()`.")
3813 .def_static(
3814 "file",
3815 [](std::string filename, int startLine, int startCol, int endLine,
3816 int endCol, DefaultingPyMlirContext context) {
3817 return PyFileLineColLocation(
3818 context->getRef(),
3820 context->get(), toMlirStringRef(filename), startLine,
3821 startCol, endLine, endCol));
3822 },
3823 "filename"_a, "start_line"_a, "start_col"_a, "end_line"_a,
3824 "end_col"_a, "context"_a = nb::none(),
3825 "Alias for `FileLineColLoc.get()` over a range.")
3826 .def_static(
3827 "name",
3828 [](std::string name, std::optional<PyLocation> childLoc,
3829 DefaultingPyMlirContext context) {
3830 return PyNameLocation(
3831 context->getRef(),
3833 context->get(), toMlirStringRef(name),
3834 childLoc ? childLoc->get()
3835 : mlirLocationUnknownGet(context->get())));
3836 },
3837 "name"_a, "childLoc"_a = nb::none(), "context"_a = nb::none(),
3838 "Alias for `NameLoc.get()`.")
3839 .def_static(
3840 "callsite",
3841 [](PyLocation callee, const std::vector<PyLocation> &frames,
3842 DefaultingPyMlirContext context) {
3843 if (frames.empty())
3844 throw nb::value_error("No caller frames provided.");
3845 MlirLocation caller = frames.back().get();
3846 for (size_t index = frames.size() - 1; index-- > 0;)
3847 caller = mlirLocationCallSiteGet(frames[index].get(), caller);
3848 return PyCallSiteLocation(
3849 context->getRef(),
3850 mlirLocationCallSiteGet(callee.get(), caller));
3851 },
3852 "callee"_a, "frames"_a, "context"_a = nb::none(),
3853 "Alias for `CallSiteLoc.get()`.")
3854 .def_static(
3855 "fused",
3856 [](const std::vector<PyLocation> &pyLocations,
3857 std::optional<PyAttribute> metadata,
3858 DefaultingPyMlirContext context) {
3859 std::vector<MlirLocation> locations;
3860 locations.reserve(pyLocations.size());
3861 for (const PyLocation &pyLocation : pyLocations)
3862 locations.push_back(pyLocation.get());
3863 MlirLocation location = mlirLocationFusedGet(
3864 context->get(), locations.size(), locations.data(),
3865 metadata ? metadata->get() : MlirAttribute{0});
3866 return PyLocation(context->getRef(), location).maybeDownCast();
3867 },
3868 "locations"_a, "metadata"_a = nb::none(), "context"_a = nb::none(),
3869 "Alias for `FusedLoc.get()` (may collapse to a non-fused location).")
3870 .def_prop_ro(
3871 "context",
3872 [](PyLocation &self) -> nb::typed<nb::object, PyMlirContext> {
3873 return self.getContext().getObject();
3874 },
3875 "Context that owns the `Location`.")
3876 .def_prop_ro(
3877 "attr",
3878 [](PyLocation &self) {
3879 return PyAttribute(self.getContext(),
3881 },
3882 "Get the underlying `LocationAttr`.")
3883 .def_prop_ro(
3884 "typeid",
3885 [](PyLocation &self) {
3886 MlirTypeID mlirTypeID =
3888 assert(!mlirTypeIDIsNull(mlirTypeID) &&
3889 "mlirTypeID was expected to be non-null.");
3890 return PyTypeID(mlirTypeID);
3891 },
3892 "Gets the `TypeID` of the underlying LocationAttr.")
3893 .def(
3894 "emit_error",
3895 [](PyLocation &self, std::string message) {
3896 mlirEmitError(self, message.c_str());
3897 },
3898 "message"_a,
3899 R"(
3900 Emits an error diagnostic at this location.
3901
3902 Args:
3903 message: The error message to emit.)")
3904 .def(
3905 "emit_remark",
3906 [](PyLocation &self, PyRemarkKind kind, const std::string &remarkName,
3907 const std::string &category, const std::string &subCategory,
3908 const std::string &functionName, const std::string &message,
3909 const std::vector<std::pair<std::string, std::string>> &args)
3910 -> bool {
3911 auto remarkKind = static_cast<MlirRemarkKind>(kind);
3912 std::vector<MlirStringRef> keys, values;
3913 keys.reserve(args.size());
3914 values.reserve(args.size());
3915 for (const auto &[key, value] : args) {
3916 keys.push_back(toMlirStringRef(key));
3917 values.push_back(toMlirStringRef(value));
3918 }
3920 self, remarkKind, toMlirStringRef(remarkName),
3921 toMlirStringRef(category), toMlirStringRef(subCategory),
3922 toMlirStringRef(functionName), toMlirStringRef(message),
3923 static_cast<intptr_t>(keys.size()), keys.data(), values.data());
3924 },
3925 "kind"_a, "remark_name"_a, nb::kw_only(), "category"_a = "",
3926 "sub_category"_a = "", "function_name"_a = "", "message"_a = "",
3927 "args"_a = std::vector<std::pair<std::string, std::string>>(),
3928 R"(
3929 Emits an optimization remark at this location through the remark
3930 engine of the context (see `Context.enable_remarks`).
3931
3932 Args:
3933 kind: The `RemarkKind` of the remark.
3934 remark_name: The name identifying the remark.
3935 category: The category the filters are matched against.
3936 sub_category: An optional sub-category.
3937 function_name: The function the remark refers to.
3938 message: Free text attached to the remark.
3939 args: Key/value pairs attached to the remark.
3940
3941 Returns:
3942 True if the remark was reported; False when no remark engine is
3943 enabled or the category is filtered out.)")
3944 .def(
3945 "__str__",
3946 [](PyLocation &self) {
3947 PyPrintAccumulator printAccum;
3948 mlirLocationPrint(self, printAccum.getCallback(),
3949 printAccum.getUserData());
3950 return printAccum.join();
3951 },
3952 "Returns the assembly form of the Location.")
3953 .def(
3954 "__repr__",
3955 [](PyLocation &self) {
3956 PyPrintAccumulator printAccum;
3957 mlirLocationPrint(self, printAccum.getCallback(),
3958 printAccum.getUserData());
3959 return printAccum.join();
3960 },
3961 "Returns the assembly representation of the location.");
3962
3968
3969 //----------------------------------------------------------------------------
3970 // Mapping of Module
3971 //----------------------------------------------------------------------------
3972 nb::class_<PyModule>(m, "Module", nb::is_weak_referenceable())
3974 "Gets a capsule wrapping the MlirModule.")
3976 R"(
3977 Creates a Module from a `MlirModule` wrapped by a capsule (i.e. `module._CAPIPtr`).
3978
3979 This returns a new object **BUT** `_clear_mlir_module(module)` must be called to
3980 prevent double-frees (of the underlying `mlir::Module`).)")
3981 .def("_clear_mlir_module", &PyModule::clearMlirModule,
3982 R"(
3983 Clears the internal MLIR module reference.
3984
3985 This is used internally to prevent double-free when ownership is transferred
3986 via the C API capsule mechanism. Not intended for normal use.)")
3987 .def_static(
3988 "parse",
3989 [](const std::string &moduleAsm, DefaultingPyMlirContext context)
3990 -> nb::typed<nb::object, PyModule> {
3991 PyMlirContext::ErrorCapture errors(context->getRef());
3992 MlirModule module = mlirModuleCreateParse(
3993 context->get(), toMlirStringRef(moduleAsm));
3994 if (mlirModuleIsNull(module))
3995 throw MLIRError("Unable to parse module assembly", errors.take());
3996 return PyModule::forModule(module).releaseObject();
3997 },
3998 "asm"_a, "context"_a = nb::none(), kModuleParseDocstring)
3999 .def_static(
4000 "parse",
4001 [](nb::bytes moduleAsm, DefaultingPyMlirContext context)
4002 -> nb::typed<nb::object, PyModule> {
4003 PyMlirContext::ErrorCapture errors(context->getRef());
4004 MlirModule module = mlirModuleCreateParse(
4005 context->get(), toMlirStringRef(moduleAsm));
4006 if (mlirModuleIsNull(module))
4007 throw MLIRError("Unable to parse module assembly", errors.take());
4008 return PyModule::forModule(module).releaseObject();
4009 },
4010 "asm"_a, "context"_a = nb::none(), kModuleParseDocstring)
4011 .def_static(
4012 "parseFile",
4013 [](const std::string &path, DefaultingPyMlirContext context)
4014 -> nb::typed<nb::object, PyModule> {
4015 PyMlirContext::ErrorCapture errors(context->getRef());
4016 MlirModule module = mlirModuleCreateParseFromFile(
4017 context->get(), toMlirStringRef(path));
4018 if (mlirModuleIsNull(module))
4019 throw MLIRError("Unable to parse module assembly", errors.take());
4020 return PyModule::forModule(module).releaseObject();
4021 },
4022 "path"_a, "context"_a = nb::none(), kModuleParseDocstring)
4023 .def_static(
4024 "create",
4025 [](const std::optional<PyLocation> &loc)
4026 -> nb::typed<nb::object, PyModule> {
4027 PyLocation pyLoc = maybeGetTracebackLocation(loc);
4028 MlirModule module = mlirModuleCreateEmpty(pyLoc.get());
4029 return PyModule::forModule(module).releaseObject();
4030 },
4031 "loc"_a = nb::none(), "Creates an empty module.")
4032 .def_prop_ro(
4033 "context",
4034 [](PyModule &self) -> nb::typed<nb::object, PyMlirContext> {
4035 return self.getContext().getObject();
4036 },
4037 "Context that created the `Module`.")
4038 .def_prop_ro(
4039 "operation",
4040 [](PyModule &self) -> nb::typed<nb::object, PyOperation> {
4041 return PyOperation::forOperation(self.getContext(),
4042 mlirModuleGetOperation(self.get()),
4043 self.getRef().releaseObject())
4044 .releaseObject();
4045 },
4046 "Accesses the module as an operation.")
4047 .def_prop_ro(
4048 "body",
4049 [](PyModule &self) {
4051 self.getContext(), mlirModuleGetOperation(self.get()),
4052 self.getRef().releaseObject());
4053 PyBlock returnBlock(moduleOp, mlirModuleGetBody(self.get()));
4054 return returnBlock;
4055 },
4056 "Return the block for this module.")
4057 .def(
4058 "dump",
4059 [](PyModule &self) {
4061 },
4063 .def(
4064 "__str__",
4065 [](const nb::object &self) {
4066 // Defer to the operation's __str__.
4067 return self.attr("operation").attr("__str__")();
4068 },
4069 nb::sig("def __str__(self) -> str"),
4070 R"(
4071 Gets the assembly form of the operation with default options.
4072
4073 If more advanced control over the assembly formatting or I/O options is needed,
4074 use the dedicated print or get_asm method, which supports keyword arguments to
4075 customize behavior.
4076 )")
4077 .def(
4078 "__eq__",
4079 [](PyModule &self, PyModule &other) {
4080 return mlirModuleEqual(self.get(), other.get());
4081 },
4082 "other"_a, "Compares two modules for equality.")
4083 .def(
4084 "__hash__",
4085 [](PyModule &self) { return mlirModuleHashValue(self.get()); },
4086 "Returns the hash value of the module.");
4087
4088 //----------------------------------------------------------------------------
4089 // Mapping of Operation.
4090 //----------------------------------------------------------------------------
4091 nb::class_<PyOperationBase>(m, "_OperationBase")
4092 .def_prop_ro(
4094 [](PyOperationBase &self) {
4095 return self.getOperation().getCapsule();
4096 },
4097 "Gets a capsule wrapping the `MlirOperation`.")
4098 .def(
4099 "__eq__",
4100 [](PyOperationBase &self, PyOperationBase &other) {
4101 return mlirOperationEqual(self.getOperation().get(),
4102 other.getOperation().get());
4103 },
4104 "Compares two operations for equality.")
4105 .def(
4106 "__eq__",
4107 [](PyOperationBase &self, nb::object other) { return false; },
4108 "Compares operation with non-operation object (always returns "
4109 "False).")
4110 .def(
4111 "__hash__",
4112 [](PyOperationBase &self) {
4113 return mlirOperationHashValue(self.getOperation().get());
4114 },
4115 "Returns the hash value of the operation.")
4116 .def(
4117 "is_structurally_equivalent",
4118 [](PyOperationBase &self, PyOperationBase &other,
4120 self.getOperation().checkValid();
4121 other.getOperation().checkValid();
4123 self.getOperation().get(), other.getOperation().get(),
4124 static_cast<uint32_t>(flags));
4125 },
4126 "other"_a, "flags"_a = PyOperationEquivalenceFlags::None,
4127 R"("Checks whether two operations are structurally equivalent. The predicate recursively compares regions.")")
4128 .def(
4129 "structural_hash",
4131 self.getOperation().checkValid();
4133 self.getOperation().get(), static_cast<uint32_t>(flags));
4134 },
4136 R"(Computes a structural hash for the operation. The hash does not recurse into regions, unlike the predicate.")")
4137 .def_prop_ro(
4138 "attributes",
4139 [](PyOperationBase &self) {
4140 return PyOpAttributeMap(self.getOperation().getRef());
4141 },
4142 "Returns a dictionary-like map of operation attributes.")
4143 .def_prop_ro(
4144 "context",
4145 [](PyOperationBase &self) -> nb::typed<nb::object, PyMlirContext> {
4146 PyOperation &concreteOperation = self.getOperation();
4147 concreteOperation.checkValid();
4148 return concreteOperation.getContext().getObject();
4149 },
4150 "Context that owns the operation.")
4151 .def_prop_ro(
4152 "name",
4153 [](PyOperationBase &self) {
4154 auto &concreteOperation = self.getOperation();
4155 concreteOperation.checkValid();
4156 MlirOperation operation = concreteOperation.get();
4157 return mlirIdentifierStr(mlirOperationGetName(operation));
4158 },
4159 "Returns the fully qualified name of the operation.")
4160 .def_prop_ro(
4161 "operands",
4162 [](PyOperationBase &self) {
4163 return PyOpOperandList(self.getOperation().getRef());
4164 },
4165 "Returns the list of operation operands.")
4166 .def_prop_ro(
4167 "op_operands",
4168 [](PyOperationBase &self) {
4169 return PyOpOperands(self.getOperation().getRef());
4170 },
4171 "Returns the list of op operands.")
4172 .def_prop_ro(
4173 "regions",
4174 [](PyOperationBase &self) {
4175 return PyRegionList(self.getOperation().getRef());
4176 },
4177 "Returns the list of operation regions.")
4178 .def_prop_ro(
4179 "results",
4180 [](PyOperationBase &self) {
4181 return PyOpResultList(self.getOperation().getRef());
4182 },
4183 "Returns the list of Operation results.")
4184 .def_prop_ro(
4185 "result",
4186 [](PyOperationBase &self) -> nb::typed<nb::object, PyOpResult> {
4187 auto &operation = self.getOperation();
4188 return PyOpResult(operation.getRef(), getUniqueResult(operation))
4189 .maybeDownCast();
4190 },
4191 "Shortcut to get an op result if it has only one (throws an error "
4192 "otherwise).")
4193 .def_prop_rw(
4194 "location",
4195 [](PyOperationBase &self) {
4196 PyOperation &operation = self.getOperation();
4197 return PyLocation(operation.getContext(),
4198 mlirOperationGetLocation(operation.get()))
4199 .maybeDownCast();
4200 },
4201 [](PyOperationBase &self, const PyLocation &location) {
4202 PyOperation &operation = self.getOperation();
4203 mlirOperationSetLocation(operation.get(), location.get());
4204 },
4205 nb::for_getter("Returns the source location the operation was "
4206 "defined or derived from."),
4207 nb::for_setter("Sets the source location the operation was defined "
4208 "or derived from."))
4209 .def_prop_ro(
4210 "parent",
4211 [](PyOperationBase &self)
4212 -> std::optional<nb::typed<nb::object, PyOperation>> {
4213 auto parent = self.getOperation().getParentOperation();
4214 if (parent)
4215 return parent->getObject();
4216 return {};
4217 },
4218 "Returns the parent operation, or `None` if at top level.")
4219 .def(
4220 "__str__",
4221 [](PyOperationBase &self) {
4222 return self.getAsm(/*binary=*/false,
4223 /*largeElementsLimit=*/std::nullopt,
4224 /*largeResourceLimit=*/std::nullopt,
4225 /*enableDebugInfo=*/false,
4226 /*prettyDebugInfo=*/false,
4227 /*printGenericOpForm=*/false,
4228 /*useLocalScope=*/false,
4229 /*useNameLocAsPrefix=*/false,
4230 /*assumeVerified=*/false,
4231 /*skipRegions=*/false);
4232 },
4233 nb::sig("def __str__(self) -> str"),
4234 "Returns the assembly form of the operation.")
4235 .def("print",
4236 nb::overload_cast<PyAsmState &, nb::object, bool>(
4238 "state"_a, "file"_a = nb::none(), "binary"_a = false,
4239 R"(
4240 Prints the assembly form of the operation to a file like object.
4241
4242 Args:
4243 state: `AsmState` capturing the operation numbering and flags.
4244 file: Optional file like object to write to. Defaults to sys.stdout.
4245 binary: Whether to write `bytes` (True) or `str` (False). Defaults to False.)")
4246 .def("print",
4247 nb::overload_cast<std::optional<int64_t>, std::optional<int64_t>,
4248 bool, bool, bool, bool, bool, bool, nb::object,
4249 bool, bool>(&PyOperationBase::print),
4250 // Careful: Lots of arguments must match up with print method.
4251 "large_elements_limit"_a = nb::none(),
4252 "large_resource_limit"_a = nb::none(), "enable_debug_info"_a = false,
4253 "pretty_debug_info"_a = false, "print_generic_op_form"_a = false,
4254 "use_local_scope"_a = false, "use_name_loc_as_prefix"_a = false,
4255 "assume_verified"_a = false, "file"_a = nb::none(),
4256 "binary"_a = false, "skip_regions"_a = false,
4257 R"(
4258 Prints the assembly form of the operation to a file like object.
4259
4260 Args:
4261 large_elements_limit: Whether to elide elements attributes above this
4262 number of elements. Defaults to None (no limit).
4263 large_resource_limit: Whether to elide resource attributes above this
4264 number of characters. Defaults to None (no limit). If large_elements_limit
4265 is set and this is None, the behavior will be to use large_elements_limit
4266 as large_resource_limit.
4267 enable_debug_info: Whether to print debug/location information. Defaults
4268 to False.
4269 pretty_debug_info: Whether to format debug information for easier reading
4270 by a human (warning: the result is unparseable). Defaults to False.
4271 print_generic_op_form: Whether to print the generic assembly forms of all
4272 ops. Defaults to False.
4273 use_local_scope: Whether to print in a way that is more optimized for
4274 multi-threaded access but may not be consistent with how the overall
4275 module prints.
4276 use_name_loc_as_prefix: Whether to use location attributes (NameLoc) as
4277 prefixes for the SSA identifiers. Defaults to False.
4278 assume_verified: By default, if not printing generic form, the verifier
4279 will be run and if it fails, generic form will be printed with a comment
4280 about failed verification. While a reasonable default for interactive use,
4281 for systematic use, it is often better for the caller to verify explicitly
4282 and report failures in a more robust fashion. Set this to True if doing this
4283 in order to avoid running a redundant verification. If the IR is actually
4284 invalid, behavior is undefined.
4285 file: The file like object to write to. Defaults to sys.stdout.
4286 binary: Whether to write bytes (True) or str (False). Defaults to False.
4287 skip_regions: Whether to skip printing regions. Defaults to False.)")
4288 .def("write_bytecode", &PyOperationBase::writeBytecode, "file"_a,
4289 "desired_version"_a = nb::none(),
4290 R"(
4291 Write the bytecode form of the operation to a file like object.
4292
4293 Args:
4294 file: The file like object to write to.
4295 desired_version: Optional version of bytecode to emit.
4296 Returns:
4297 The bytecode writer status.)")
4298 .def("get_asm", &PyOperationBase::getAsm,
4299 // Careful: Lots of arguments must match up with get_asm method.
4300 "binary"_a = false, "large_elements_limit"_a = nb::none(),
4301 "large_resource_limit"_a = nb::none(), "enable_debug_info"_a = false,
4302 "pretty_debug_info"_a = false, "print_generic_op_form"_a = false,
4303 "use_local_scope"_a = false, "use_name_loc_as_prefix"_a = false,
4304 "assume_verified"_a = false, "skip_regions"_a = false,
4305 R"(
4306 Gets the assembly form of the operation with all options available.
4307
4308 Args:
4309 binary: Whether to return a bytes (True) or str (False) object. Defaults to
4310 False.
4311 ... others ...: See the print() method for common keyword arguments for
4312 configuring the printout.
4313 Returns:
4314 Either a bytes or str object, depending on the setting of the `binary`
4315 argument.)")
4316 .def("verify", &PyOperationBase::verify,
4317 "Verify the operation. Raises MLIRError if verification fails, and "
4318 "returns true otherwise.")
4319 .def("move_after", &PyOperationBase::moveAfter, "other"_a,
4320 "Puts self immediately after the other operation in its parent "
4321 "block.")
4322 .def("move_before", &PyOperationBase::moveBefore, "other"_a,
4323 "Puts self immediately before the other operation in its parent "
4324 "block.")
4325 .def("is_before_in_block", &PyOperationBase::isBeforeInBlock, "other"_a,
4326 R"(
4327 Checks if this operation is before another in the same block.
4328
4329 Args:
4330 other: Another operation in the same parent block.
4331
4332 Returns:
4333 True if this operation is before `other` in the operation list of the parent block.)")
4334 .def(
4335 "clone",
4336 [](PyOperationBase &self,
4337 const nb::object &ip) -> nb::typed<nb::object, PyOperation> {
4338 return self.getOperation().clone(ip);
4339 },
4340 "ip"_a = nb::none(),
4341 R"(
4342 Creates a deep copy of the operation.
4343
4344 Args:
4345 ip: Optional insertion point where the cloned operation should be inserted.
4346 If None, the current insertion point is used. If False, the operation
4347 remains detached.
4348
4349 Returns:
4350 A new Operation that is a clone of this operation.)")
4351 .def(
4352 "detach_from_parent",
4353 [](PyOperationBase &self) -> nb::typed<nb::object, PyOpView> {
4354 PyOperation &operation = self.getOperation();
4355 operation.checkValid();
4356 if (!operation.isAttached())
4357 throw nb::value_error("Detached operation has no parent.");
4358
4359 operation.detachFromParent();
4360 return operation.createOpView();
4361 },
4362 "Detaches the operation from its parent block.")
4363 .def_prop_ro(
4364 "attached",
4365 [](PyOperationBase &self) {
4366 PyOperation &operation = self.getOperation();
4367 operation.checkValid();
4368 return operation.isAttached();
4369 },
4370 "Reports if the operation is attached to its parent block.")
4371 .def(
4372 "erase", [](PyOperationBase &self) { self.getOperation().erase(); },
4373 R"(
4374 Erases the operation and frees its memory.
4375
4376 Note:
4377 After erasing, any Python references to the operation become invalid.)")
4378 .def(
4379 "walk",
4380 [](PyOperationBase &self,
4381 std::function<PyWalkResult(MlirOperation)> callback,
4382 PyWalkOrder walkOrder, std::optional<nb::object> opClass) {
4383 if (!opClass)
4384 return self.walk(callback, walkOrder);
4385 self.walk(
4386 [&](MlirOperation mlirOp) -> PyWalkResult {
4387 nb::object opview =
4389 self.getOperation().getContext(), mlirOp)
4390 ->createOpView();
4391 if (nb::isinstance(opview, *opClass))
4392 return callback(mlirOp);
4393 return PyWalkResult::Advance;
4394 },
4395 walkOrder);
4396 },
4397 "callback"_a, "walk_order"_a = PyWalkOrder::PostOrder,
4398 "op_class"_a = nb::none(),
4399 // clang-format off
4400 nb::sig("def walk(self, callback: Callable[[Operation], WalkResult], walk_order: WalkOrder = ..., op_class: type[OpView] | None = None) -> None"),
4401 // clang-format on
4402 R"(
4403 Walks the operation tree with a callback function.
4404
4405 If op_class is provided, the callback is only invoked on operations
4406 of that type; all other operations are skipped silently.
4407
4408 Args:
4409 callback: A callable that takes an Operation and returns a WalkResult.
4410 walk_order: The order of traversal (PRE_ORDER or POST_ORDER).
4411 op_class: If provided, only operations of this type are passed to the callback.)")
4412 .def(
4413 "has_trait",
4414 [](PyOperationBase &self, nb::type_object &traitCls) {
4415 PyTypeID traitTypeID =
4416 nb::cast<PyTypeID>(traitCls.attr(PyDynamicOpTrait::typeIDAttr));
4417 MlirIdentifier opName =
4418 mlirOperationGetName(self.getOperation().get());
4420 mlirIdentifierStr(opName), traitTypeID.get(),
4421 self.getOperation().getContext()->get());
4422 },
4423 "trait_cls"_a, "Checks if the operation has a given trait.");
4424
4425 nb::class_<PyOperation, PyOperationBase>(m, "Operation")
4426 .def_static(
4427 "create",
4428 [](std::string_view name,
4429 std::optional<std::vector<PyType *>> results,
4430 std::optional<std::vector<PyValue *>> operands,
4431 std::optional<nb::typed<nb::dict, nb::str, PyAttribute>>
4432 attributes,
4433 std::optional<std::vector<PyBlock *>> successors, int regions,
4434 const std::optional<PyLocation> &location,
4435 const nb::object &maybeIp,
4436 bool inferType) -> nb::typed<nb::object, PyOperation> {
4437 // Unpack/validate operands.
4438 std::vector<MlirValue> mlirOperands;
4439 if (operands) {
4440 mlirOperands.reserve(operands->size());
4441 for (PyValue *operand : *operands) {
4442 if (!operand)
4443 throw nb::value_error("operand value cannot be None");
4444 mlirOperands.push_back(operand->get());
4445 }
4446 }
4447
4448 PyLocation pyLoc = maybeGetTracebackLocation(location);
4449 return PyOperation::create(
4450 name, results, mlirOperands.data(), mlirOperands.size(),
4451 attributes, successors, regions, pyLoc, maybeIp, inferType);
4452 },
4453 "name"_a, "results"_a = nb::none(), "operands"_a = nb::none(),
4454 "attributes"_a = nb::none(), "successors"_a = nb::none(),
4455 "regions"_a = 0, "loc"_a = nb::none(), "ip"_a = nb::none(),
4456 "infer_type"_a = false,
4457 R"(
4458 Creates a new operation.
4459
4460 Args:
4461 name: Operation name (e.g. `dialect.operation`).
4462 results: Optional sequence of Type representing op result types.
4463 operands: Optional operands of the operation.
4464 attributes: Optional Dict of {str: Attribute}.
4465 successors: Optional List of Block for the operation's successors.
4466 regions: Number of regions to create (default = 0).
4467 location: Optional Location object (defaults to resolve from context manager).
4468 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).
4469 infer_type: Whether to infer result types (default = False).
4470 Returns:
4471 A new detached Operation object. Detached operations can be added to blocks, which causes them to become attached.)")
4472 .def_static(
4473 "parse",
4474 [](const std::string &sourceStr, const std::string &sourceName,
4476 -> nb::typed<nb::object, PyOpView> {
4477 return PyOperation::parse(context->getRef(), sourceStr, sourceName)
4478 ->createOpView();
4479 },
4480 "source"_a, nb::kw_only(), "source_name"_a = "",
4481 "context"_a = nb::none(),
4482 "Parses an operation. Supports both text assembly format and binary "
4483 "bytecode format.")
4485 "Gets a capsule wrapping the MlirOperation.")
4488 "Creates an Operation from a capsule wrapping MlirOperation.")
4489 .def_prop_ro(
4490 "operation",
4491 [](nb::object self) -> nb::typed<nb::object, PyOperation> {
4492 return self;
4493 },
4494 "Returns self (the operation).")
4495 .def_prop_ro(
4496 "opview",
4497 [](PyOperation &self) -> nb::typed<nb::object, PyOpView> {
4498 return self.createOpView();
4499 },
4500 R"(
4501 Returns an OpView of this operation.
4502
4503 Note:
4504 If the operation has a registered and loaded dialect then this OpView will
4505 be concrete wrapper class.)")
4506 .def_prop_ro("block", &PyOperation::getBlock,
4507 "Returns the block containing this operation.")
4508 .def_prop_ro(
4509 "successors",
4510 [](PyOperationBase &self) {
4511 return PyOpSuccessors(self.getOperation().getRef());
4512 },
4513 "Returns the list of Operation successors.")
4514 .def(
4515 "replace_uses_of_with",
4516 [](PyOperation &self, PyValue &of, PyValue &with) {
4517 mlirOperationReplaceUsesOfWith(self.get(), of.get(), with.get());
4518 },
4519 "of"_a, "with_"_a,
4520 "Replaces uses of the 'of' value with the 'with' value inside the "
4521 "operation.")
4522 .def("_set_invalid", &PyOperation::setInvalid,
4523 "Invalidate the operation.");
4524
4525 auto opViewClass =
4526 nb::class_<PyOpView, PyOperationBase>(m, "OpView")
4527 .def(nb::init<nb::typed<nb::object, PyOperation>>(), "operation"_a)
4528 .def(
4529 "__init__",
4530 [](PyOpView *self, std::string_view name,
4531 std::tuple<int, bool> opRegionSpec,
4532 nb::object operandSegmentSpecObj,
4533 nb::object resultSegmentSpecObj,
4534 std::optional<nb::sequence> resultTypeList,
4535 nb::sequence operandList,
4536 std::optional<nb::typed<nb::dict, nb::str, PyAttribute>>
4537 attributes,
4538 std::optional<std::vector<PyBlock *>> successors,
4539 std::optional<int> regions,
4540 const std::optional<PyLocation> &location,
4541 const nb::object &maybeIp) {
4542 PyLocation pyLoc = maybeGetTracebackLocation(location);
4544 name, opRegionSpec, operandSegmentSpecObj,
4545 resultSegmentSpecObj, resultTypeList, operandList,
4546 attributes, successors, regions, pyLoc, maybeIp));
4547 },
4548 "name"_a, "opRegionSpec"_a,
4549 "operandSegmentSpecObj"_a = nb::none(),
4550 "resultSegmentSpecObj"_a = nb::none(), "results"_a = nb::none(),
4551 "operands"_a = nb::none(), "attributes"_a = nb::none(),
4552 "successors"_a = nb::none(), "regions"_a = nb::none(),
4553 "loc"_a = nb::none(), "ip"_a = nb::none())
4554 .def_prop_ro(
4555 "operation",
4556 [](PyOpView &self) -> nb::typed<nb::object, PyOperation> {
4557 return self.getOperationObject();
4558 })
4559 .def_prop_ro("opview",
4560 [](nb::object self) -> nb::typed<nb::object, PyOpView> {
4561 return self;
4562 })
4563 .def(
4564 "__str__",
4565 [](PyOpView &self) { return nb::str(self.getOperationObject()); })
4566 .def_prop_ro(
4567 "successors",
4568 [](PyOperationBase &self) {
4569 return PyOpSuccessors(self.getOperation().getRef());
4570 },
4571 "Returns the list of Operation successors.")
4572 .def(
4573 "_set_invalid",
4574 [](PyOpView &self) { self.getOperation().setInvalid(); },
4575 "Invalidate the operation.");
4576 opViewClass.attr("_ODS_REGIONS") = nb::make_tuple(0, true);
4577 opViewClass.attr("_ODS_OPERAND_SEGMENTS") = nb::none();
4578 opViewClass.attr("_ODS_RESULT_SEGMENTS") = nb::none();
4579 // It is faster to pass the operation_name, ods_regions, and
4580 // ods_operand_segments/ods_result_segments as arguments to the constructor,
4581 // rather than to access them as attributes.
4582 opViewClass.attr("build_generic") = classmethod(
4583 [](nb::handle cls, std::optional<nb::sequence> resultTypeList,
4584 nb::sequence operandList,
4585 std::optional<nb::typed<nb::dict, nb::str, PyAttribute>> attributes,
4586 std::optional<std::vector<PyBlock *>> successors,
4587 std::optional<int> regions, std::optional<PyLocation> location,
4588 const nb::object &maybeIp) {
4589 std::string name = nb::cast<std::string>(cls.attr("OPERATION_NAME"));
4590 std::tuple<int, bool> opRegionSpec =
4591 nb::cast<std::tuple<int, bool>>(cls.attr("_ODS_REGIONS"));
4592 nb::object operandSegmentSpec = cls.attr("_ODS_OPERAND_SEGMENTS");
4593 nb::object resultSegmentSpec = cls.attr("_ODS_RESULT_SEGMENTS");
4594 PyLocation pyLoc = maybeGetTracebackLocation(location);
4595 return PyOpView::buildGeneric(name, opRegionSpec, operandSegmentSpec,
4596 resultSegmentSpec, resultTypeList,
4597 operandList, attributes, successors,
4598 regions, pyLoc, maybeIp);
4599 },
4600 "cls"_a, "results"_a = nb::none(), "operands"_a = nb::none(),
4601 "attributes"_a = nb::none(), "successors"_a = nb::none(),
4602 "regions"_a = nb::none(), "loc"_a = nb::none(), "ip"_a = nb::none(),
4603 // clang-format off
4604 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"),
4605 // clang-format on
4606 "Builds a specific, generated OpView based on class level attributes.");
4607 opViewClass.attr("parse") = classmethod(
4608 [](const nb::object &cls, const std::string &sourceStr,
4609 const std::string &sourceName,
4610 DefaultingPyMlirContext context) -> nb::typed<nb::object, PyOpView> {
4611 PyOperationRef parsed =
4612 PyOperation::parse(context->getRef(), sourceStr, sourceName);
4613
4614 // Check if the expected operation was parsed, and cast to to the
4615 // appropriate `OpView` subclass if successful.
4616 // NOTE: This accesses attributes that have been automatically added to
4617 // `OpView` subclasses, and is not intended to be used on `OpView`
4618 // directly.
4619 std::string clsOpName =
4620 nb::cast<std::string>(cls.attr("OPERATION_NAME"));
4621 MlirStringRef identifier =
4623 std::string_view parsedOpName(identifier.data, identifier.length);
4624 if (clsOpName != parsedOpName)
4625 throw MLIRError(join("Expected a '", clsOpName, "' op, got: '",
4626 parsedOpName, "'"));
4627 return PyOpView::constructDerived(cls, parsed.getObject());
4628 },
4629 "cls"_a, "source"_a, nb::kw_only(), "source_name"_a = "",
4630 "context"_a = nb::none(),
4631 // clang-format off
4632 nb::sig("def parse(cls, source: str, *, source_name: str = '', context: Context | None = None) -> typing.Self"),
4633 // clang-format on
4634 "Parses a specific, generated OpView based on class level attributes.");
4635 opViewClass.attr("has_trait") = classmethod(
4636 [](nb::object &self, nb::type_object &traitCls,
4637 DefaultingPyMlirContext &context) {
4638 PyTypeID traitTypeID =
4639 nb::cast<PyTypeID>(traitCls.attr(PyDynamicOpTrait::typeIDAttr));
4640 std::string opName = nb::cast<std::string>(self.attr("OPERATION_NAME"));
4642 mlirStringRefCreate(opName.data(), opName.size()),
4643 traitTypeID.get(), context->get());
4644 },
4645 "cls"_a, "trait_cls"_a, "context"_a = nb::none(),
4646 "Checks if the operation has a given trait.");
4647
4649
4650 //----------------------------------------------------------------------------
4651 // Mapping of PyRegion.
4652 //----------------------------------------------------------------------------
4653 nb::class_<PyRegion>(m, "Region")
4654 .def_prop_ro(
4655 "blocks",
4656 [](PyRegion &self) {
4657 return PyBlockList(self.getParentOperation(), self.get());
4658 },
4659 "Returns a forward-optimized sequence of blocks.")
4660 .def_prop_ro(
4661 "owner",
4662 [](PyRegion &self) -> nb::typed<nb::object, PyOpView> {
4663 return self.getParentOperation()->createOpView();
4664 },
4665 "Returns the operation owning this region.")
4666 .def(
4667 "__iter__",
4668 [](PyRegion &self) {
4669 self.checkValid();
4670 MlirBlock firstBlock = mlirRegionGetFirstBlock(self.get());
4671 return PyBlockIterator(self.getParentOperation(), firstBlock);
4672 },
4673 "Iterates over blocks in the region.")
4674 .def(
4675 "__eq__",
4676 [](PyRegion &self, PyRegion &other) {
4677 return self.get().ptr == other.get().ptr;
4678 },
4679 "Compares two regions for pointer equality.")
4680 .def(
4681 "__eq__", [](PyRegion &self, nb::object &other) { return false; },
4682 "Compares region with non-region object (always returns False).");
4683
4684 //----------------------------------------------------------------------------
4685 // Mapping of PyBlock.
4686 //----------------------------------------------------------------------------
4687 nb::class_<PyBlock>(m, "Block")
4689 "Gets a capsule wrapping the MlirBlock.")
4690 .def_prop_ro(
4691 "owner",
4692 [](PyBlock &self) -> nb::typed<nb::object, PyOpView> {
4693 return self.getParentOperation()->createOpView();
4694 },
4695 "Returns the owning operation of this block.")
4696 .def_prop_ro(
4697 "region",
4698 [](PyBlock &self) {
4699 MlirRegion region = mlirBlockGetParentRegion(self.get());
4700 return PyRegion(self.getParentOperation(), region);
4701 },
4702 "Returns the owning region of this block.")
4703 .def_prop_ro(
4704 "arguments",
4705 [](PyBlock &self) {
4706 return PyBlockArgumentList(self.getParentOperation(), self.get());
4707 },
4708 "Returns a list of block arguments.")
4709 .def(
4710 "add_argument",
4711 [](PyBlock &self, const PyType &type, const PyLocation &loc) {
4712 return PyBlockArgument(self.getParentOperation(),
4713 mlirBlockAddArgument(self.get(), type, loc));
4714 },
4715 "type"_a, "loc"_a,
4716 R"(
4717 Appends an argument of the specified type to the block.
4718
4719 Args:
4720 type: The type of the argument to add.
4721 loc: The source location for the argument.
4722
4723 Returns:
4724 The newly added block argument.)")
4725 .def(
4726 "erase_argument",
4727 [](PyBlock &self, unsigned index) {
4728 return mlirBlockEraseArgument(self.get(), index);
4729 },
4730 "index"_a,
4731 R"(
4732 Erases the argument at the specified index.
4733
4734 Args:
4735 index: The index of the argument to erase.)")
4736 .def_prop_ro(
4737 "operations",
4738 [](PyBlock &self) {
4739 return PyOperationList(self.getParentOperation(), self.get());
4740 },
4741 "Returns a forward-optimized sequence of operations.")
4742 .def_static(
4743 "create_at_start",
4744 [](PyRegion &parent, nb::typed<nb::sequence, PyType> pyArgTypes,
4745 const std::optional<nb::typed<nb::sequence, PyLocation>>
4746 &pyArgLocs) {
4747 parent.checkValid();
4748 MlirBlock block = createBlock(pyArgTypes, pyArgLocs);
4749 mlirRegionInsertOwnedBlock(parent, 0, block);
4750 return PyBlock(parent.getParentOperation(), block);
4751 },
4752 "parent"_a, "arg_types"_a = nb::list(), "arg_locs"_a = std::nullopt,
4753 "Creates and returns a new Block at the beginning of the given "
4754 "region (with given argument types and locations).")
4755 .def(
4756 "append_to",
4757 [](PyBlock &self, PyRegion &region) {
4758 MlirBlock b = self.get();
4761 mlirRegionAppendOwnedBlock(region.get(), b);
4762 },
4763 "region"_a,
4764 R"(
4765 Appends this block to a region.
4766
4767 Transfers ownership if the block is currently owned by another region.
4768
4769 Args:
4770 region: The region to append the block to.)")
4771 .def(
4772 "create_before",
4773 [](PyBlock &self, const nb::args &pyArgTypes,
4774 const std::optional<nb::typed<nb::sequence, PyLocation>>
4775 &pyArgLocs) {
4776 self.checkValid();
4777 MlirBlock block =
4778 createBlock(nb::cast<nb::sequence>(pyArgTypes), pyArgLocs);
4779 MlirRegion region = mlirBlockGetParentRegion(self.get());
4780 mlirRegionInsertOwnedBlockBefore(region, self.get(), block);
4781 return PyBlock(self.getParentOperation(), block);
4782 },
4783 "arg_types"_a, nb::kw_only(), "arg_locs"_a = std::nullopt,
4784 "Creates and returns a new Block before this block "
4785 "(with given argument types and locations).")
4786 .def(
4787 "create_after",
4788 [](PyBlock &self, const nb::args &pyArgTypes,
4789 const std::optional<nb::typed<nb::sequence, PyLocation>>
4790 &pyArgLocs) {
4791 self.checkValid();
4792 MlirBlock block =
4793 createBlock(nb::cast<nb::sequence>(pyArgTypes), pyArgLocs);
4794 MlirRegion region = mlirBlockGetParentRegion(self.get());
4795 mlirRegionInsertOwnedBlockAfter(region, self.get(), block);
4796 return PyBlock(self.getParentOperation(), block);
4797 },
4798 "arg_types"_a, nb::kw_only(), "arg_locs"_a = std::nullopt,
4799 "Creates and returns a new Block after this block "
4800 "(with given argument types and locations).")
4801 .def(
4802 "__iter__",
4803 [](PyBlock &self) {
4804 self.checkValid();
4805 MlirOperation firstOperation =
4806 mlirBlockGetFirstOperation(self.get());
4807 return PyOperationIterator(self.getParentOperation(),
4808 firstOperation);
4809 },
4810 "Iterates over operations in the block.")
4811 .def(
4812 "__eq__",
4813 [](PyBlock &self, PyBlock &other) {
4814 return self.get().ptr == other.get().ptr;
4815 },
4816 "Compares two blocks for pointer equality.")
4817 .def(
4818 "__eq__", [](PyBlock &self, nb::object &other) { return false; },
4819 "Compares block with non-block object (always returns False).")
4820 .def(
4821 "__hash__", [](PyBlock &self) { return hash(self.get().ptr); },
4822 "Returns the hash value of the block.")
4823 .def(
4824 "__str__",
4825 [](PyBlock &self) {
4826 self.checkValid();
4827 PyPrintAccumulator printAccum;
4828 mlirBlockPrint(self.get(), printAccum.getCallback(),
4829 printAccum.getUserData());
4830 return printAccum.join();
4831 },
4832 "Returns the assembly form of the block.")
4833 .def(
4834 "append",
4835 [](PyBlock &self, PyOperationBase &operation) {
4836 if (operation.getOperation().isAttached())
4837 operation.getOperation().detachFromParent();
4838
4839 MlirOperation mlirOperation = operation.getOperation().get();
4840 mlirBlockAppendOwnedOperation(self.get(), mlirOperation);
4841 operation.getOperation().setAttached(
4842 self.getParentOperation().getObject());
4843 },
4844 "operation"_a,
4845 R"(
4846 Appends an operation to this block.
4847
4848 If the operation is currently in another block, it will be moved.
4849
4850 Args:
4851 operation: The operation to append to the block.)")
4852 .def_prop_ro(
4853 "successors",
4854 [](PyBlock &self) {
4855 return PyBlockSuccessors(self, self.getParentOperation());
4856 },
4857 "Returns the list of Block successors.")
4858 .def_prop_ro(
4859 "predecessors",
4860 [](PyBlock &self) {
4861 return PyBlockPredecessors(self, self.getParentOperation());
4862 },
4863 "Returns the list of Block predecessors.");
4864
4865 //----------------------------------------------------------------------------
4866 // Mapping of PyInsertionPoint.
4867 //----------------------------------------------------------------------------
4868
4869 nb::class_<PyInsertionPoint>(m, "InsertionPoint")
4870 .def(nb::init<PyBlock &>(), "block"_a,
4871 "Inserts after the last operation but still inside the block.")
4872 .def("__enter__", &PyInsertionPoint::contextEnter,
4873 "Enters the insertion point as a context manager.",
4874 nb::sig("def __enter__(self, /) -> InsertionPoint"))
4875 .def("__exit__", &PyInsertionPoint::contextExit, "exc_type"_a.none(),
4876 "exc_value"_a.none(), "traceback"_a.none(),
4877 "Exits the insertion point context manager.")
4878 .def_prop_ro_static(
4879 "current",
4880 [](nb::object & /*class*/) {
4882 if (!ip)
4883 throw nb::value_error("No current InsertionPoint");
4884 return ip;
4885 },
4886 nb::sig("def current(/) -> InsertionPoint"),
4887 "Gets the InsertionPoint bound to the current thread or raises "
4888 "ValueError if none has been set.")
4889 .def(nb::init<PyOperationBase &>(), "beforeOperation"_a,
4890 "Inserts before a referenced operation.")
4891 .def_static("at_block_begin", &PyInsertionPoint::atBlockBegin, "block"_a,
4892 R"(
4893 Creates an insertion point at the beginning of a block.
4894
4895 Args:
4896 block: The block at whose beginning operations should be inserted.
4897
4898 Returns:
4899 An InsertionPoint at the block's beginning.)")
4900 .def_static("at_block_terminator", &PyInsertionPoint::atBlockTerminator,
4901 "block"_a,
4902 R"(
4903 Creates an insertion point before a block's terminator.
4904
4905 Args:
4906 block: The block whose terminator to insert before.
4907
4908 Returns:
4909 An InsertionPoint before the terminator.
4910
4911 Raises:
4912 ValueError: If the block has no terminator.)")
4913 .def_static("after", &PyInsertionPoint::after, "operation"_a,
4914 R"(
4915 Creates an insertion point immediately after an operation.
4916
4917 Args:
4918 operation: The operation after which to insert.
4919
4920 Returns:
4921 An InsertionPoint after the operation.)")
4922 .def("insert", &PyInsertionPoint::insert, "operation"_a,
4923 R"(
4924 Inserts an operation at this insertion point.
4925
4926 Args:
4927 operation: The operation to insert.)")
4928 .def_prop_ro(
4929 "block", [](PyInsertionPoint &self) { return self.getBlock(); },
4930 "Returns the block that this `InsertionPoint` points to.")
4931 .def_prop_ro(
4932 "ref_operation",
4933 [](PyInsertionPoint &self)
4934 -> std::optional<nb::typed<nb::object, PyOperation>> {
4935 auto refOperation = self.getRefOperation();
4936 if (refOperation)
4937 return refOperation->getObject();
4938 return {};
4939 },
4940 "The reference operation before which new operations are "
4941 "inserted, or None if the insertion point is at the end of "
4942 "the block.");
4943
4944 //----------------------------------------------------------------------------
4945 // Mapping of PyAttribute.
4946 //----------------------------------------------------------------------------
4947 nb::class_<PyAttribute>(m, "Attribute")
4948 // Delegate to the PyAttribute copy constructor, which will also lifetime
4949 // extend the backing context which owns the MlirAttribute.
4950 .def(nb::init<PyAttribute &>(), "cast_from_type"_a,
4951 "Casts the passed attribute to the generic `Attribute`.")
4953 "Gets a capsule wrapping the MlirAttribute.")
4954 .def_static(
4956 "Creates an Attribute from a capsule wrapping `MlirAttribute`.")
4957 .def_static(
4958 "parse",
4959 [](const std::string &attrSpec, DefaultingPyMlirContext context)
4960 -> nb::typed<nb::object, PyAttribute> {
4961 PyMlirContext::ErrorCapture errors(context->getRef());
4962 MlirAttribute attr = mlirAttributeParseGet(
4963 context->get(), toMlirStringRef(attrSpec));
4964 if (mlirAttributeIsNull(attr))
4965 throw MLIRError("Unable to parse attribute", errors.take());
4966 return PyAttribute(context.get()->getRef(), attr).maybeDownCast();
4967 },
4968 "asm"_a, "context"_a = nb::none(),
4969 "Parses an attribute from an assembly form. Raises an `MLIRError` on "
4970 "failure.")
4971 .def_prop_ro(
4972 "context",
4973 [](PyAttribute &self) -> nb::typed<nb::object, PyMlirContext> {
4974 return self.getContext().getObject();
4975 },
4976 "Context that owns the `Attribute`.")
4977 .def_prop_ro(
4978 "type",
4979 [](PyAttribute &self) -> nb::typed<nb::object, PyType> {
4980 return PyType(self.getContext(), mlirAttributeGetType(self))
4981 .maybeDownCast();
4982 },
4983 "Returns the type of the `Attribute`.")
4984 .def(
4985 "get_named",
4986 [](PyAttribute &self, std::string name) {
4987 return PyNamedAttribute(self, std::move(name));
4988 },
4989 nb::keep_alive<0, 1>(),
4990 R"(
4991 Binds a name to the attribute, creating a `NamedAttribute`.
4992
4993 Args:
4994 name: The name to bind to the `Attribute`.
4995
4996 Returns:
4997 A `NamedAttribute` with the given name and this attribute.)")
4998 .def(
4999 "__eq__",
5000 [](PyAttribute &self, PyAttribute &other) { return self == other; },
5001 "Compares two attributes for equality.")
5002 .def(
5003 "__eq__", [](PyAttribute &self, nb::object &other) { return false; },
5004 "Compares attribute with non-attribute object (always returns "
5005 "False).")
5006 .def(
5007 "__hash__", [](PyAttribute &self) { return hash(self.get().ptr); },
5008 "Returns the hash value of the attribute.")
5009 .def(
5010 "dump", [](PyAttribute &self) { mlirAttributeDump(self); },
5012 .def(
5013 "__str__",
5014 [](PyAttribute &self) {
5015 PyPrintAccumulator printAccum;
5016 mlirAttributePrint(self, printAccum.getCallback(),
5017 printAccum.getUserData());
5018 return printAccum.join();
5019 },
5020 "Returns the assembly form of the Attribute.")
5021 .def(
5022 "__repr__",
5023 [](PyAttribute &self) {
5024 // Generally, assembly formats are not printed for __repr__ because
5025 // this can cause exceptionally long debug output and exceptions.
5026 // However, attribute values are generally considered useful and
5027 // are printed. This may need to be re-evaluated if debug dumps end
5028 // up being excessive.
5029 PyPrintAccumulator printAccum;
5030 printAccum.parts.append("Attribute(");
5031 mlirAttributePrint(self, printAccum.getCallback(),
5032 printAccum.getUserData());
5033 printAccum.parts.append(")");
5034 return printAccum.join();
5035 },
5036 "Returns a string representation of the attribute.")
5037 .def_prop_ro(
5038 "typeid",
5039 [](PyAttribute &self) {
5040 MlirTypeID mlirTypeID = mlirAttributeGetTypeID(self);
5041 assert(!mlirTypeIDIsNull(mlirTypeID) &&
5042 "mlirTypeID was expected to be non-null.");
5043 return PyTypeID(mlirTypeID);
5044 },
5045 "Returns the `TypeID` of the attribute.")
5046 .def(
5048 [](PyAttribute &self) -> nb::typed<nb::object, PyAttribute> {
5049 return self.maybeDownCast();
5050 },
5051 "Downcasts the attribute to a more specific attribute if possible.");
5052
5053 //----------------------------------------------------------------------------
5054 // Mapping of PyNamedAttribute
5055 //----------------------------------------------------------------------------
5056 nb::class_<PyNamedAttribute>(m, "NamedAttribute")
5057 .def(
5058 "__repr__",
5059 [](PyNamedAttribute &self) {
5060 PyPrintAccumulator printAccum;
5061 printAccum.parts.append("NamedAttribute(");
5062 printAccum.parts.append(
5063 nb::str(mlirIdentifierStr(self.namedAttr.name).data,
5064 mlirIdentifierStr(self.namedAttr.name).length));
5065 printAccum.parts.append("=");
5066 mlirAttributePrint(self.namedAttr.attribute,
5067 printAccum.getCallback(),
5068 printAccum.getUserData());
5069 printAccum.parts.append(")");
5070 return printAccum.join();
5071 },
5072 "Returns a string representation of the named attribute.")
5073 .def_prop_ro(
5074 "name",
5075 [](PyNamedAttribute &self) {
5076 return mlirIdentifierStr(self.namedAttr.name);
5077 },
5078 "The name of the `NamedAttribute` binding.")
5079 .def_prop_ro(
5080 "attr",
5081 [](PyNamedAttribute &self) { return self.namedAttr.attribute; },
5082 nb::keep_alive<0, 1>(), nb::sig("def attr(self) -> Attribute"),
5083 "The underlying generic attribute of the `NamedAttribute` binding.");
5084
5085 //----------------------------------------------------------------------------
5086 // Mapping of PyType.
5087 //----------------------------------------------------------------------------
5088 nb::class_<PyType>(m, "Type")
5089 // Delegate to the PyType copy constructor, which will also lifetime
5090 // extend the backing context which owns the MlirType.
5091 .def(nb::init<PyType &>(), "cast_from_type"_a,
5092 "Casts the passed type to the generic `Type`.")
5094 "Gets a capsule wrapping the `MlirType`.")
5096 "Creates a Type from a capsule wrapping `MlirType`.")
5097 .def_static(
5098 "parse",
5099 [](std::string typeSpec,
5100 DefaultingPyMlirContext context) -> nb::typed<nb::object, PyType> {
5101 PyMlirContext::ErrorCapture errors(context->getRef());
5102 MlirType type =
5103 mlirTypeParseGet(context->get(), toMlirStringRef(typeSpec));
5104 if (mlirTypeIsNull(type))
5105 throw MLIRError("Unable to parse type", errors.take());
5106 return PyType(context.get()->getRef(), type).maybeDownCast();
5107 },
5108 "asm"_a, "context"_a = nb::none(),
5109 R"(
5110 Parses the assembly form of a type.
5111
5112 Returns a Type object or raises an `MLIRError` if the type cannot be parsed.
5113
5114 See also: https://mlir.llvm.org/docs/LangRef/#type-system)")
5115 .def_prop_ro(
5116 "context",
5117 [](PyType &self) -> nb::typed<nb::object, PyMlirContext> {
5118 return self.getContext().getObject();
5119 },
5120 "Context that owns the `Type`.")
5121 .def(
5122 "__eq__", [](PyType &self, PyType &other) { return self == other; },
5123 "Compares two types for equality.")
5124 .def(
5125 "__eq__", [](PyType &self, nb::object &other) { return false; },
5126 "other"_a.none(),
5127 "Compares type with non-type object (always returns False).")
5128 .def(
5129 "__hash__", [](PyType &self) { return hash(self.get().ptr); },
5130 "Returns the hash value of the `Type`.")
5131 .def(
5132 "dump", [](PyType &self) { mlirTypeDump(self); }, kDumpDocstring)
5133 .def(
5134 "__str__",
5135 [](PyType &self) {
5136 PyPrintAccumulator printAccum;
5137 mlirTypePrint(self, printAccum.getCallback(),
5138 printAccum.getUserData());
5139 return printAccum.join();
5140 },
5141 "Returns the assembly form of the `Type`.")
5142 .def(
5143 "__repr__",
5144 [](PyType &self) {
5145 // Generally, assembly formats are not printed for __repr__ because
5146 // this can cause exceptionally long debug output and exceptions.
5147 // However, types are an exception as they typically have compact
5148 // assembly forms and printing them is useful.
5149 PyPrintAccumulator printAccum;
5150 printAccum.parts.append("Type(");
5151 mlirTypePrint(self, printAccum.getCallback(),
5152 printAccum.getUserData());
5153 printAccum.parts.append(")");
5154 return printAccum.join();
5155 },
5156 "Returns a string representation of the `Type`.")
5157 .def(
5159 [](PyType &self) -> nb::typed<nb::object, PyType> {
5160 return self.maybeDownCast();
5161 },
5162 "Downcasts the Type to a more specific `Type` if possible.")
5163 .def_prop_ro(
5164 "typeid",
5165 [](PyType &self) {
5166 MlirTypeID mlirTypeID = mlirTypeGetTypeID(self);
5167 if (!mlirTypeIDIsNull(mlirTypeID))
5168 return PyTypeID(mlirTypeID);
5169 auto origRepr = nb::cast<std::string>(nb::repr(nb::cast(self)));
5170 throw nb::value_error(join(origRepr, " has no typeid.").c_str());
5171 },
5172 "Returns the `TypeID` of the `Type`, or raises `ValueError` if "
5173 "`Type` has no "
5174 "`TypeID`.");
5175
5176 //----------------------------------------------------------------------------
5177 // Mapping of PyTypeID.
5178 //----------------------------------------------------------------------------
5179 nb::class_<PyTypeID>(m, "TypeID")
5181 "Gets a capsule wrapping the `MlirTypeID`.")
5183 "Creates a `TypeID` from a capsule wrapping `MlirTypeID`.")
5184 // Note, this tests whether the underlying TypeIDs are the same,
5185 // not whether the wrapper MlirTypeIDs are the same, nor whether
5186 // the Python objects are the same (i.e., PyTypeID is a value type).
5187 .def(
5188 "__eq__",
5189 [](PyTypeID &self, PyTypeID &other) { return self == other; },
5190 "Compares two `TypeID`s for equality.")
5191 .def(
5192 "__eq__",
5193 [](PyTypeID &self, const nb::object &other) { return false; },
5194 "Compares TypeID with non-TypeID object (always returns False).")
5195 // Note, this gives the hash value of the underlying TypeID, not the
5196 // hash value of the Python object, nor the hash value of the
5197 // MlirTypeID wrapper.
5198 .def(
5199 "__hash__",
5200 [](PyTypeID &self) {
5201 return static_cast<size_t>(mlirTypeIDHashValue(self));
5202 },
5203 "Returns the hash value of the `TypeID`.");
5204
5205 //----------------------------------------------------------------------------
5206 // Mapping of Value.
5207 //----------------------------------------------------------------------------
5208 m.attr("_T") = nb::type_var("_T", "bound"_a = m.attr("Type"));
5209
5210 nb::class_<PyValue>(m, "Value", nb::is_generic(),
5211 nb::sig("class Value(typing.Generic[_T])"))
5212 .def(nb::init<PyValue &>(), nb::keep_alive<0, 1>(), "value"_a,
5213 "Creates a Value reference from another `Value`.")
5215 "Gets a capsule wrapping the `MlirValue`.")
5217 "Creates a `Value` from a capsule wrapping `MlirValue`.")
5218 .def_prop_ro(
5219 "context",
5220 [](PyValue &self) -> nb::typed<nb::object, PyMlirContext> {
5221 return self.getParentOperation()->getContext().getObject();
5222 },
5223 "Context in which the value lives.")
5224 .def(
5225 "dump", [](PyValue &self) { mlirValueDump(self.get()); },
5227 .def_prop_ro(
5228 "owner",
5229 [](PyValue &self)
5230 -> nb::typed<nb::object, std::variant<PyOpView, PyBlock>> {
5231 MlirValue v = self.get();
5232 if (mlirValueIsAOpResult(v)) {
5233 assert(mlirOperationEqual(self.getParentOperation()->get(),
5234 mlirOpResultGetOwner(self.get())) &&
5235 "expected the owner of the value in Python to match "
5236 "that in "
5237 "the IR");
5238 return self.getParentOperation()->createOpView();
5239 }
5240
5242 MlirBlock block = mlirBlockArgumentGetOwner(self.get());
5243 return nb::cast(PyBlock(self.getParentOperation(), block));
5244 }
5245
5246 assert(false && "Value must be a block argument or an op result");
5247 return nb::none();
5248 },
5249 "Returns the owner of the value (`Operation` for results, `Block` "
5250 "for "
5251 "arguments).")
5252 .def_prop_ro(
5253 "uses",
5254 [](PyValue &self) {
5255 return PyOpOperandIterator(mlirValueGetFirstUse(self.get()));
5256 },
5257 "Returns an iterator over uses of this value.")
5258 .def(
5259 "__eq__",
5260 [](PyValue &self, PyValue &other) {
5261 return self.get().ptr == other.get().ptr;
5262 },
5263 "Compares two values for pointer equality.")
5264 .def(
5265 "__eq__", [](PyValue &self, nb::object other) { return false; },
5266 "Compares value with non-value object (always returns False).")
5267 .def(
5268 "__hash__", [](PyValue &self) { return hash(self.get().ptr); },
5269 "Returns the hash value of the value.")
5270 .def(
5271 "__str__",
5272 [](PyValue &self) {
5273 PyPrintAccumulator printAccum;
5274 printAccum.parts.append("Value(");
5275 mlirValuePrint(self.get(), printAccum.getCallback(),
5276 printAccum.getUserData());
5277 printAccum.parts.append(")");
5278 return printAccum.join();
5279 },
5280 R"(
5281 Returns the string form of the value.
5282
5283 If the value is a block argument, this is the assembly form of its type and the
5284 position in the argument list. If the value is an operation result, this is
5285 equivalent to printing the operation that produced it.
5286 )")
5287 .def(
5288 "get_name",
5289 [](PyValue &self, bool useLocalScope, bool useNameLocAsPrefix) {
5290 PyPrintAccumulator printAccum;
5291 MlirOpPrintingFlags flags = mlirOpPrintingFlagsCreate();
5292 if (useLocalScope)
5294 if (useNameLocAsPrefix)
5296 MlirAsmState valueState =
5297 mlirAsmStateCreateForValue(self.get(), flags);
5298 mlirValuePrintAsOperand(self.get(), valueState,
5299 printAccum.getCallback(),
5300 printAccum.getUserData());
5302 mlirAsmStateDestroy(valueState);
5303 return printAccum.join();
5304 },
5305 "use_local_scope"_a = false, "use_name_loc_as_prefix"_a = false,
5306 R"(
5307 Returns the string form of value as an operand.
5308
5309 Args:
5310 use_local_scope: Whether to use local scope for naming.
5311 use_name_loc_as_prefix: Whether to use the location attribute (NameLoc) as prefix.
5312
5313 Returns:
5314 The value's name as it appears in IR (e.g., `%0`, `%arg0`).)")
5315 .def(
5316 "get_name",
5317 [](PyValue &self, PyAsmState &state) {
5318 PyPrintAccumulator printAccum;
5319 MlirAsmState valueState = state.get();
5320 mlirValuePrintAsOperand(self.get(), valueState,
5321 printAccum.getCallback(),
5322 printAccum.getUserData());
5323 return printAccum.join();
5324 },
5325 "state"_a,
5326 "Returns the string form of value as an operand (i.e., the ValueID).")
5327 .def_prop_ro(
5328 "type",
5329 [](PyValue &self) {
5330 return PyType(self.getParentOperation()->getContext(),
5331 mlirValueGetType(self.get()))
5332 .maybeDownCast();
5333 },
5334 "Returns the type of the value.", nb::sig("def type(self) -> _T"))
5335 .def(
5336 "set_type",
5337 [](PyValue &self, const PyType &type) {
5338 mlirValueSetType(self.get(), type);
5339 },
5340 "type"_a, "Sets the type of the value.",
5341 nb::sig("def set_type(self, type: _T)"))
5342 .def(
5343 "replace_all_uses_with",
5344 [](PyValue &self, PyValue &with) {
5345 mlirValueReplaceAllUsesOfWith(self.get(), with.get());
5346 },
5347 "Replace all uses of value with the new value, updating anything in "
5348 "the IR that uses `self` to use the other value instead.")
5349 .def(
5350 "replace_all_uses_except",
5351 [](PyValue &self, PyValue &with, PyOperation &exception) {
5352 MlirOperation exceptedUser = exception.get();
5353 mlirValueReplaceAllUsesExcept(self, with, 1, &exceptedUser);
5354 },
5355 "with_"_a, "exceptions"_a, kValueReplaceAllUsesExceptDocstring)
5356 .def(
5357 "replace_all_uses_except",
5358 [](PyValue &self, PyValue &with,
5359 std::vector<PyOperation> &exceptions) {
5360 // Convert Python list to a std::vector of MlirOperations
5361 std::vector<MlirOperation> exceptionOps;
5362 for (PyOperation &exception : exceptions)
5363 exceptionOps.push_back(exception);
5365 self, with, static_cast<intptr_t>(exceptionOps.size()),
5366 exceptionOps.data());
5367 },
5368 "with_"_a, "exceptions"_a, kValueReplaceAllUsesExceptDocstring)
5369 .def(
5371 [](PyValue &self) { return self.maybeDownCast(); },
5372 "Downcasts the `Value` to a more specific kind if possible.")
5373 .def_prop_ro(
5374 "location",
5375 [](PyValue self) {
5376 return PyLocation(
5379 .maybeDownCast();
5380 },
5381 "Returns the source location of the value.");
5382
5386
5387 nb::class_<PyAsmState>(m, "AsmState")
5388 .def(nb::init<PyValue &, bool>(), "value"_a, "use_local_scope"_a = false,
5389 R"(
5390 Creates an `AsmState` for consistent SSA value naming.
5391
5392 Args:
5393 value: The value to create state for.
5394 use_local_scope: Whether to use local scope for naming.)")
5395 .def(nb::init<PyOperationBase &, bool>(), "op"_a,
5396 "use_local_scope"_a = false,
5397 R"(
5398 Creates an AsmState for consistent SSA value naming.
5399
5400 Args:
5401 op: The operation to create state for.
5402 use_local_scope: Whether to use local scope for naming.)");
5403
5404 //----------------------------------------------------------------------------
5405 // Mapping of SymbolTable.
5406 //----------------------------------------------------------------------------
5407 nb::class_<PySymbolTable>(m, "SymbolTable")
5408 .def(nb::init<PyOperationBase &>(),
5409 R"(
5410 Creates a symbol table for an operation.
5411
5412 Args:
5413 operation: The `Operation` that defines a symbol table (e.g., a `ModuleOp`).
5414
5415 Raises:
5416 TypeError: If the operation is not a symbol table.)")
5417 .def(
5418 "__getitem__",
5419 [](PySymbolTable &self,
5420 const std::string &name) -> nb::typed<nb::object, PyOpView> {
5421 return self.dunderGetItem(name);
5422 },
5423 R"(
5424 Looks up a symbol by name in the symbol table.
5425
5426 Args:
5427 name: The name of the symbol to look up.
5428
5429 Returns:
5430 The operation defining the symbol.
5431
5432 Raises:
5433 KeyError: If the symbol is not found.)")
5434 .def("insert", &PySymbolTable::insert, "operation"_a,
5435 R"(
5436 Inserts a symbol operation into the symbol table.
5437
5438 Args:
5439 operation: An operation with a symbol name to insert.
5440
5441 Returns:
5442 The symbol name attribute of the inserted operation.
5443
5444 Raises:
5445 ValueError: If the operation does not have a symbol name.)")
5446 .def("erase", &PySymbolTable::erase, "operation"_a,
5447 R"(
5448 Erases a symbol operation from the symbol table.
5449
5450 Args:
5451 operation: The symbol operation to erase.
5452
5453 Note:
5454 The operation is also erased from the IR and invalidated.)")
5455 .def("__delitem__", &PySymbolTable::dunderDel,
5456 "Deletes a symbol by name from the symbol table.")
5457 .def(
5458 "__contains__",
5459 [](PySymbolTable &table, const std::string &name) {
5460 return !mlirOperationIsNull(mlirSymbolTableLookup(
5461 table, mlirStringRefCreate(name.data(), name.length())));
5462 },
5463 "Checks if a symbol with the given name exists in the table.")
5464 // Static helpers.
5465 .def_static("set_symbol_name", &PySymbolTable::setSymbolName, "symbol"_a,
5466 "name"_a, "Sets the symbol name for a symbol operation.")
5467 .def_static("get_symbol_name", &PySymbolTable::getSymbolName, "symbol"_a,
5468 "Gets the symbol name from a symbol operation.")
5469 .def_static("get_visibility", &PySymbolTable::getVisibility, "symbol"_a,
5470 "Gets the visibility attribute of a symbol operation.")
5471 .def_static("set_visibility", &PySymbolTable::setVisibility, "symbol"_a,
5472 "visibility"_a,
5473 "Sets the visibility attribute of a symbol operation.")
5474 .def_static("replace_all_symbol_uses",
5475 &PySymbolTable::replaceAllSymbolUses, "old_symbol"_a,
5476 "new_symbol"_a, "from_op"_a,
5477 "Replaces all uses of a symbol with a new symbol name within "
5478 "the given operation.")
5479 .def_static("walk_symbol_tables", &PySymbolTable::walkSymbolTables,
5480 "from_op"_a, "all_sym_uses_visible"_a, "callback"_a,
5481 "Walks symbol tables starting from an operation with a "
5482 "callback function.");
5483
5484 // Container bindings.
5499
5500 // Debug bindings.
5502
5503 // Attribute builder getter.
5505
5506 // Extensible Dialect
5512
5513 // MLIRError exception.
5514 MLIRError::bind(m);
5515
5516 // Register an atexit handler to clear the thread-local context stack.
5517 // The stack holds nb::object references that prevent Python GC of Contexts.
5518 // At interpreter shutdown, thread_local storage outlives Py_Finalize() on
5519 // the main thread. When the thread_local vector destructs, its nb::object
5520 // members call Py_DECREF through the dead runtime, causing a segfault.
5521 // Clearing the stack in atexit releases references while alive.
5522 nb::module_::import_("atexit").attr("register")(
5523 nb::cpp_function([]() { PyThreadContextEntry::getStack().clear(); }));
5524}
5525} // namespace MLIR_BINDINGS_PYTHON_DOMAIN
5526} // namespace python
5527} // 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:34
static size_t hash(const T &value)
Local helper to compute std::hash for a value.
Definition IRCore.cpp:57
static nb::object createCustomDialectWrapper(const std::string &dialectNamespace, nb::object dialectDescriptor)
Definition IRCore.cpp:62
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
static const char kValueReplaceAllUsesExceptDocstring[]
Definition IRCore.cpp:45
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:1158
MlirIdentifier mlirOperationGetName(MlirOperation op)
Definition IR.cpp:719
bool mlirValueIsABlockArgument(MlirValue value)
Definition IR.cpp:1178
intptr_t mlirOperationGetNumRegions(MlirOperation op)
Definition IR.cpp:739
MlirBlock mlirOperationGetBlock(MlirOperation op)
Definition IR.cpp:723
void mlirBlockArgumentSetType(MlirValue value, MlirType type)
Definition IR.cpp:1195
void mlirOperationStateAddAttributes(MlirOperationState *state, intptr_t n, MlirNamedAttribute const *attributes)
Definition IR.cpp:546
MlirValue mlirOperationGetResult(MlirOperation op, intptr_t pos)
Definition IR.cpp:794
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:790
void mlirOperationDestroy(MlirOperation op)
Definition IR.cpp:664
MlirContext mlirAttributeGetContext(MlirAttribute attribute)
Definition IR.cpp:1353
MlirType mlirValueGetType(MlirValue value)
Definition IR.cpp:1214
void mlirBlockPrint(MlirBlock block, MlirStringCallback callback, void *userData)
Definition IR.cpp:1144
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:855
MlirOperation mlirOperationGetNextInBlock(MlirOperation op)
Definition IR.cpp:763
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:1190
MlirBlock mlirOperationGetSuccessor(MlirOperation op, intptr_t pos)
Definition IR.cpp:802
bool mlirAttributeEqual(MlirAttribute a1, MlirAttribute a2)
Definition IR.cpp:1372
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:1154
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:1218
intptr_t mlirOperationGetNumSuccessors(MlirOperation op)
Definition IR.cpp:798
MlirDialect mlirAttributeGetDialect(MlirAttribute attr)
Definition IR.cpp:1368
void mlirLocationPrint(MlirLocation location, MlirStringCallback callback, void *userData)
Definition IR.cpp:440
void mlirOperationSetAttributeByName(MlirOperation op, MlirStringRef name, MlirAttribute attr)
Definition IR.cpp:874
void mlirOperationSetOperand(MlirOperation op, intptr_t pos, MlirValue newValue)
Definition IR.cpp:779
MlirOperation mlirOpResultGetOwner(MlirValue value)
Definition IR.cpp:1205
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:1186
void mlirBlockArgumentSetLocation(MlirValue value, MlirLocation loc)
Definition IR.cpp:1200
MlirValue mlirOperationGetOperand(MlirOperation op, intptr_t pos)
Definition IR.cpp:771
MlirOpOperand mlirOperationGetOpOperand(MlirOperation op, intptr_t pos)
Definition IR.cpp:775
MlirLocation mlirOperationGetLocation(MlirOperation op)
Definition IR.cpp:705
MlirAttribute mlirOperationGetAttributeByName(MlirOperation op, MlirStringRef name)
Definition IR.cpp:869
MlirTypeID mlirAttributeGetTypeID(MlirAttribute attr)
Definition IR.cpp:1364
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:1357
bool mlirOperationRemoveAttributeByName(MlirOperation op, MlirStringRef name)
Definition IR.cpp:879
bool mlirValueIsAOpResult(MlirValue value)
Definition IR.cpp:1182
MlirBlock mlirBlockGetPredecessor(MlirBlock block, intptr_t pos)
Definition IR.cpp:1163
size_t mlirOperationStructuralHashValue(MlirOperation op, uint32_t flags)
Definition IR.cpp:688
MlirRegion mlirOperationGetRegion(MlirOperation op, intptr_t pos)
Definition IR.cpp:743
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:1349
void mlirOperationRemoveFromParent(MlirOperation op)
Definition IR.cpp:666
intptr_t mlirBlockGetNumSuccessors(MlirBlock block)
Definition IR.cpp:1150
MlirNamedAttribute mlirOperationGetAttribute(MlirOperation op, intptr_t pos)
Definition IR.cpp:864
void mlirOpPrintingFlagsDestroy(MlirOpPrintingFlags flags)
Definition IR.cpp:223
void mlirValueDump(MlirValue value)
Definition IR.cpp:1222
void mlirTypePrint(MlirType type, MlirStringCallback callback, void *userData)
Definition IR.cpp:1338
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:1209
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:767
void mlirTypeDump(MlirType type)
Definition IR.cpp:1343
intptr_t mlirOperationGetNumAttributes(MlirOperation op)
Definition IR.cpp:860
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 SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
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:351
Used in function arguments when None should resolve to the current context manager set instance.
Definition IRCore.h:332
PyAsmState(MlirValue value, bool useLocalScope)
Definition IRCore.cpp:1748
Wrapper around the generic MlirAttribute.
Definition IRCore.h:1095
PyAttribute(PyMlirContextRef contextRef, MlirAttribute attr)
Definition IRCore.h:1097
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirAttribute.
Definition IRCore.cpp:1858
bool operator==(const PyAttribute &other) const
Definition IRCore.cpp:1854
static PyAttribute createFromCapsule(const nanobind::object &capsule)
Creates a PyAttribute from the MlirAttribute wrapped by a capsule.
Definition IRCore.cpp:1862
nanobind::typed< nanobind::object, PyAttribute > maybeDownCast()
Definition IRCore.cpp:1870
PyBlockArgumentList(PyOperationRef operation, MlirBlock block, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2204
Python wrapper for MlirBlockArgument.
Definition IRCore.h:1835
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:1630
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:2364
PyBlockSuccessors(PyBlock block, PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2341
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:499
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:511
Python class mirroring the C MlirDiagnostic struct.
Definition IRCore.h:423
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:591
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:567
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:2569
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2669
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2623
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2646
static bool attach(const nanobind::object &opName, PyMlirContext &context)
Definition IRCore.cpp:2694
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:926
void insert(PyOperationBase &operationBase)
Inserts an operation.
Definition IRCore.cpp:1779
void contextExit(const nanobind::object &excType, const nanobind::object &excVal, const nanobind::object &excTb)
Definition IRCore.cpp:1844
static PyInsertionPoint atBlockTerminator(PyBlock &block)
Shortcut to create an insertion point before the block terminator.
Definition IRCore.cpp:1818
static PyInsertionPoint after(PyOperationBase &op)
Shortcut to create an insertion point to the node after the specified operation.
Definition IRCore.cpp:1827
static PyInsertionPoint atBlockBegin(PyBlock &block)
Shortcut to create an insertion point at the beginning of the block.
Definition IRCore.cpp:1805
PyInsertionPoint(const PyBlock &block)
Creates an insertion point positioned after the last operation in the block, but still inside the blo...
Definition IRCore.cpp:1770
static nanobind::object contextEnter(nanobind::object insertionPoint)
Enter and exit the context manager.
Definition IRCore.cpp:1840
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:1888
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:360
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
bool getRemarksEnabled()
Whether a remark engine is enabled on the context.
void enableRemarks(PyRemarkPolicy policy, const std::optional< std::string > &outputFile, PyRemarkFormat format, const std::string &allFilter, const std::string &passedFilter, const std::string &missedFilter, const std::string &analysisFilter, const std::string &failedFilter, nanobind::object callback, std::optional< bool > printAsEmitRemarks)
Enables the optimization remark engine of the context, streaming to outputFile, to callback,...
Definition IRRemarks.cpp:65
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:245
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:299
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
void finalizeRemarks()
Finalizes and removes the remark engine; no-op when none is enabled.
size_t getLiveModuleCount()
Gets the count of live modules associated with this context.
Definition IRCore.cpp:1838
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:641
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:1121
PyNamedAttribute(MlirAttribute attr, std::string ownedName)
Constructs a PyNamedAttr that retains an owned name.
Definition IRCore.cpp:1905
Template for a reference to a concrete type which captures a python reference to its underlying pytho...
Definition IRCore.h:67
nanobind::object releaseObject()
Releases the object held by this instance, returning it.
Definition IRCore.h:105
void dunderSetItem(const std::string &name, const PyAttribute &attr)
Definition IRCore.cpp:2424
nanobind::typed< nanobind::object, PyAttribute > dunderGetItemNamed(const std::string &name)
Definition IRCore.cpp:2391
nanobind::typed< nanobind::object, std::optional< PyAttribute > > get(const std::string &key, nanobind::object defaultValue)
Definition IRCore.cpp:2401
static void forEachAttr(MlirOperation op, std::function< void(MlirStringRef, MlirAttribute)> fn)
Definition IRCore.cpp:2446
PyNamedAttribute dunderGetItemIndexed(intptr_t index)
Definition IRCore.cpp:2409
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:2236
void dunderSetItem(intptr_t index, PyValue value)
Definition IRCore.cpp:2244
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:2279
Sliceable< PyOpOperandList, PyOpOperand > SliceableT
Definition IRCore.cpp:2277
PyOpResultList(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:1398
PyOpSuccessors(PyOperationRef operation, intptr_t startIndex=0, intptr_t length=-1, intptr_t step=1)
Definition IRCore.cpp:2308
void dunderSetItem(intptr_t index, PyBlock block)
Definition IRCore.cpp:2316
A PyOpView is equivalent to the C++ "Op" wrappers: these are the basis for providing more instance-sp...
Definition IRCore.h:828
PyOpView(const nanobind::object &operationObject)
Definition IRCore.cpp:1738
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:1561
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:1730
Base class for PyOperation and PyOpView which exposes the primary, user visible methods for manipulat...
Definition IRCore.h:671
bool isBeforeInBlock(PyOperationBase &other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Definition IRCore.cpp:1162
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:1116
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:1015
void writeBytecode(const nanobind::object &fileObject, std::optional< int64_t > bytecodeVersion)
Definition IRCore.cpp:1063
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:1144
void walk(std::function< PyWalkResult(MlirOperation)> callback, PyWalkOrder walkOrder)
Definition IRCore.cpp:1084
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:1671
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:1226
void setInvalid()
Invalidate the operation.
Definition IRCore.h:795
PyOperation & getOperation() override
Each must provide access to the raw Operation.
Definition IRCore.h:728
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:972
nanobind::object clone(const nanobind::object &ip)
Clones this operation.
Definition IRCore.cpp:1341
static nanobind::object createFromCapsule(const nanobind::object &capsule)
Creates a PyOperation from the MlirOperation wrapped by a capsule.
Definition IRCore.cpp:1202
std::optional< PyOperationRef > getParentOperation()
Gets the parent operation or raises an exception if the operation has no parent.
Definition IRCore.cpp:1178
nanobind::object createOpView()
Creates an OpView suitable for this operation.
Definition IRCore.cpp:1350
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirOperation.
Definition IRCore.cpp:1197
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:956
void detachFromParent()
Detaches the operation from its parent block and updates its state accordingly.
Definition IRCore.cpp:984
void erase()
Erases the underlying MlirOperation, removes its pointer from the parent context's live operations ma...
Definition IRCore.cpp:1361
PyBlock getBlock()
Gets the owning block or raises an exception if the operation has no owning block.
Definition IRCore.cpp:1188
static PyOperationRef createDetached(PyMlirContextRef contextRef, MlirOperation operation, nanobind::object parentKeepAlive=nanobind::object())
Creates a detached operation.
Definition IRCore.cpp:963
PyOperation(PyMlirContextRef contextRef, MlirOperation operation)
Definition IRCore.cpp:913
void setAttached(const nanobind::object &parent=nanobind::object())
Definition IRCore.cpp:999
Regions of an op are fixed length and indexed numerically so are represented with a sequence-like con...
Definition IRCore.h:1591
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:2057
PySymbolTable(PyOperationBase &operation)
Constructs a symbol table for the given operation.
Definition IRCore.cpp:2021
static PyStringAttribute getVisibility(PyOperationBase &symbol)
Gets and sets the visibility of a symbol op.
Definition IRCore.cpp:2097
void erase(PyOperationBase &symbol)
Removes the given operation from the symbol table and erases it.
Definition IRCore.cpp:2042
static void walkSymbolTables(PyOperationBase &from, bool allSymUsesVisible, nanobind::object callback)
Walks all symbol tables under and including 'from'.
Definition IRCore.cpp:2138
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:2029
static void replaceAllSymbolUses(const std::string &oldSymbol, const std::string &newSymbol, PyOperationBase &from)
Replaces all symbol uses within an operation.
Definition IRCore.cpp:2126
static PyStringAttribute getSymbolName(PyOperationBase &symbol)
Gets and sets the name of a symbol op.
Definition IRCore.cpp:2069
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:2052
static void setSymbolName(PyOperationBase &symbol, const std::string &name)
Definition IRCore.cpp:2082
static void setVisibility(PyOperationBase &symbol, const std::string &visibility)
Definition IRCore.cpp:2108
Tracks an entry in the thread context stack.
Definition IRCore.h:138
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:194
A TypeID provides an efficient and unique identifier for a specific C++ type.
Definition IRCore.h:994
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirTypeID.
Definition IRCore.cpp:1951
bool operator==(const PyTypeID &other) const
Definition IRCore.cpp:1961
static PyTypeID createFromCapsule(nanobind::object capsule)
Creates a PyTypeID from the MlirTypeID wrapped by a capsule.
Definition IRCore.cpp:1955
Wrapper around the generic MlirType.
Definition IRCore.h:968
PyType(PyMlirContextRef contextRef, MlirType type)
Definition IRCore.h:970
bool operator==(const PyType &other) const
Definition IRCore.cpp:1917
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirType.
Definition IRCore.cpp:1921
static PyType createFromCapsule(nanobind::object capsule)
Creates a PyType from the MlirType wrapped by a capsule.
Definition IRCore.cpp:1925
nanobind::typed< nanobind::object, PyType > maybeDownCast()
Definition IRCore.cpp:1933
nanobind::object getCapsule()
Gets a capsule wrapping the void* within the MlirValue.
Definition IRCore.cpp:1969
PyValue(PyOperationRef parentOperation, MlirValue value)
Definition IRCore.h:1387
nanobind::typed< nanobind::object, std::variant< PyBlockArgument, PyOpResult, PyValue > > maybeDownCast()
Definition IRCore.cpp:1988
static PyValue createFromCapsule(nanobind::object capsule)
Creates a PyValue from the MlirValue wrapped by a capsule.
Definition IRCore.cpp:2009
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:935
MLIR_CAPI_EXPORTED MlirLocation mlirValueGetLocation(MlirValue v)
Gets the location of the value.
Definition IR.cpp:1275
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:904
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:1457
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:1436
MLIR_CAPI_EXPORTED MlirStringRef mlirSymbolTableGetDefaultVisibilityAttributeName(void)
Returns the name of the attribute used by default to store symbol visibility.
Definition IR.cpp:1417
MlirWalkOrder
Traversal order for operation walk.
Definition IR.h:928
MLIR_CAPI_EXPORTED MlirNamedAttribute mlirNamedAttributeGet(MlirIdentifier name, MlirAttribute attr)
Associates an attribute with the name. Takes ownership of neither.
Definition IR.cpp:1384
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:1441
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:1405
MLIR_CAPI_EXPORTED MlirType mlirTypeParseGet(MlirContext context, MlirStringRef type)
Parses a type. The type is owned by the context.
Definition IR.cpp:1318
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:1301
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:1014
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:1247
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:1230
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:1289
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:1376
MLIR_CAPI_EXPORTED MlirRegion mlirBlockGetParentRegion(MlirBlock block)
Returns the region that contains this block.
Definition IR.cpp:1053
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:928
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:1251
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:895
MlirWalkResult
Operation walk result.
Definition IR.h:921
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:994
static bool mlirTypeIsNull(MlirType type)
Checks whether a type is null.
Definition IR.h:1255
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:1122
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:1103
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:1000
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:1037
static bool mlirBlockIsNull(MlirBlock block)
Checks whether a block is null.
Definition IR.h:1021
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:1078
MLIR_CAPI_EXPORTED MlirValue mlirBlockGetArgument(MlirBlock block, intptr_t pos)
Returns pos-th argument of the block.
Definition IR.cpp:1140
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:1431
MLIR_CAPI_EXPORTED MlirContext mlirTypeGetContext(MlirType type)
Gets the context that a type was created with.
Definition IR.cpp:1322
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:1287
MLIR_CAPI_EXPORTED MlirSymbolTable mlirSymbolTableCreate(MlirOperation operation)
Creates a symbol table for the given operation.
Definition IR.cpp:1421
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:1126
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:889
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:1237
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:920
MLIR_CAPI_EXPORTED bool mlirTypeEqual(MlirType t1, MlirType t2)
Checks if two types are equal.
Definition IR.cpp:1334
MLIR_CAPI_EXPORTED unsigned mlirOpOperandGetOperandNumber(MlirOpOperand opOperand)
Returns the operand number of an op operand.
Definition IR.cpp:1297
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:1068
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:932
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:1326
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:1057
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:950
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:1049
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:1131
MLIR_CAPI_EXPORTED void mlirAttributeDump(MlirAttribute attr)
Prints the attribute to the standard error stream.
Definition IR.cpp:1382
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:1446
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:990
MLIR_CAPI_EXPORTED MlirOperation mlirBlockGetFirstOperation(MlirBlock block)
Returns the first operation in the block.
Definition IR.cpp:1061
static bool mlirRegionIsNull(MlirRegion region)
Checks whether a region is null.
Definition IR.h:960
MLIR_CAPI_EXPORTED MlirDialect mlirTypeGetDialect(MlirType type)
Gets the dialect a type belongs to.
Definition IR.cpp:1330
MLIR_CAPI_EXPORTED MlirIdentifier mlirIdentifierGet(MlirContext context, MlirStringRef str)
Gets an identifier with the given string value.
Definition IR.cpp:1393
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:1279
MLIR_CAPI_EXPORTED MlirStringRef mlirSymbolTableGetSymbolAttributeName(void)
Returns the name of the attribute used to store symbol names compatible with symbol tables.
Definition IR.cpp:1413
MLIR_CAPI_EXPORTED MlirRegion mlirRegionCreate(void)
Creates a new empty region and transfers ownership to the caller.
Definition IR.cpp:977
MLIR_CAPI_EXPORTED void mlirBlockDetach(MlirBlock block)
Detach a block from the owning region and assume ownership.
Definition IR.cpp:1117
MLIR_CAPI_EXPORTED void mlirOperationDump(MlirOperation op)
Prints an operation to stderr.
Definition IR.cpp:918
static bool mlirSymbolTableIsNull(MlirSymbolTable symbolTable)
Returns true if the symbol table is null.
Definition IR.h:1346
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:968
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:924
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:1224
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:911
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:983
MLIR_CAPI_EXPORTED bool mlirEmitOptimizationRemark(MlirLocation location, MlirRemarkKind kind, MlirStringRef remarkName, MlirStringRef categoryName, MlirStringRef subCategoryName, MlirStringRef functionName, MlirStringRef message, intptr_t numArgs, const MlirStringRef *argKeys, const MlirStringRef *argValues)
Emits a remark of kind at location through the remark engine of the location's context.
Definition Remarks.cpp:217
MlirRemarkKind
The outcome a remark describes (see mlir::remark::RemarkKind).
Definition Remarks.h:38
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
InFlightDiagnostic & next(InFlightDiagnostic &diag)
Starts a new message part in an in-flight diagnostic.
MLIR_PYTHON_API_EXPORTED MlirValue getUniqueResult(MlirOperation operation)
Definition IRCore.cpp:1527
static std::string formatMLIRError(const MLIRError &e)
Definition IRCore.cpp:2882
PyOperationEquivalenceFlags
Flags controlling structural operation equivalence and hashing.
Definition IRCore.h:411
MLIR_PYTHON_API_EXPORTED void populateRoot(nanobind::module_ &m)
static void maybeInsertOperation(PyOperationRef &op, const nb::object &maybeIp)
Definition IRCore.cpp:1211
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:1440
PyObjectRef< PyMlirContext > PyMlirContextRef
Wrapper around MlirContext.
Definition IRCore.h:231
static MlirValue getOpResultOrValue(nb::handle operand)
Definition IRCore.cpp:1542
PyObjectRef< PyOperation > PyOperationRef
Definition IRCore.h:723
MlirStringRef toMlirStringRef(const std::string &s)
Definition IRCore.h:1554
PyRemarkKind
The kinds, emitting policies and file formats of the remark engine (mlir-c/Remarks....
Definition IRCore.h:212
static bool attachOpTrait(const nb::object &opName, MlirDynamicOpTrait trait, PyMlirContext &context)
Definition IRCore.cpp:2554
PyObjectRef< PyModule > PyModuleRef
Definition IRCore.h:630
static MlirLogicalResult verifyTraitByMethod(MlirOperation op, void *userData, const char *methodName)
Definition IRCore.cpp:2543
static PyOperationRef getValueOwnerRef(MlirValue value)
Definition IRCore.cpp:1973
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:1387
PyWalkOrder
Traversal order for operation walk.
Definition IRCore.h:404
MLIR_PYTHON_API_EXPORTED void populateIRCore(nanobind::module_ &m)
nanobind::object classmethod(Func f, Args... args)
Helper for creating an @classmethod.
Definition IRCore.h:2093
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:1536
MLIRError(std::string message, std::vector< PyDiagnostic::DiagnosticInfo > &&errorDiagnostics={})
Definition IRCore.h:1537
std::vector< PyDiagnostic::DiagnosticInfo > errorDiagnostics
Definition IRCore.h:1547
static void bind(nanobind::module_ &m)
Bind the MLIRError exception class to the given module.
Definition IRCore.cpp:2917
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:527
std::vector< PyDiagnostic::DiagnosticInfo > take()
Definition IRCore.h:537