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