| //===- PythonTestModuleNanobind.cpp - PythonTest dialect extension --------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // This is the nanobind edition of the PythonTest dialect module. | |
| //===----------------------------------------------------------------------===// | |
| #include "PythonTestCAPI.h" | |
| #include "mlir-c/BuiltinAttributes.h" | |
| #include "mlir-c/BuiltinTypes.h" | |
| #include "mlir-c/Diagnostics.h" | |
| #include "mlir-c/IR.h" | |
| #include "mlir/Bindings/Python/Diagnostics.h" | |
| #include "mlir/Bindings/Python/IRCore.h" | |
| #include "mlir/Bindings/Python/IRTypes.h" | |
| #include "mlir/Bindings/Python/Nanobind.h" | |
| #include "mlir/Bindings/Python/NanobindAdaptors.h" | |
| #include "nanobind/nanobind.h" | |
| namespace nb = nanobind; | |
| using namespace mlir::python::nanobind_adaptors; | |
| namespace mlir { | |
| namespace python { | |
| namespace MLIR_BINDINGS_PYTHON_DOMAIN { | |
| namespace python_test { | |
| static bool mlirTypeIsARankedIntegerTensor(MlirType t) { | |
| return mlirTypeIsARankedTensor(t) && | |
| mlirTypeIsAInteger(mlirShapedTypeGetElementType(t)); | |
| } | |
| struct PyTestType : PyConcreteType<PyTestType> { | |
| static constexpr IsAFunctionTy isaFunction = mlirTypeIsAPythonTestTestType; | |
| static constexpr GetTypeIDFunctionTy getTypeIdFunction = | |
| mlirPythonTestTestTypeGetTypeID; | |
| static constexpr const char *pyClassName = "TestType"; | |
| using PyConcreteType::PyConcreteType; | |
| static void bindDerived(ClassTy &c) { | |
| c.def_static( | |
| "get", | |
| [](DefaultingPyMlirContext context) { | |
| return PyTestType(context->getRef(), | |
| mlirPythonTestTestTypeGet(context.get()->get())); | |
| }, | |
| nb::arg("context").none() = nb::none()); | |
| } | |
| }; | |
| struct PyTestIntegerRankedTensorType | |
| : PyConcreteType<PyTestIntegerRankedTensorType, PyRankedTensorType> { | |
| static constexpr IsAFunctionTy isaFunction = mlirTypeIsARankedIntegerTensor; | |
| static constexpr GetTypeIDFunctionTy getTypeIdFunction = | |
| mlirRankedTensorTypeGetTypeID; | |
| static constexpr const char *pyClassName = "TestIntegerRankedTensorType"; | |
| using PyConcreteType::PyConcreteType; | |
| static void bindDerived(ClassTy &c) { | |
| c.def_static( | |
| "get", | |
| [](std::vector<int64_t> shape, unsigned width, | |
| DefaultingPyMlirContext ctx) { | |
| MlirAttribute encoding = mlirAttributeGetNull(); | |
| return PyTestIntegerRankedTensorType( | |
| ctx->getRef(), | |
| mlirRankedTensorTypeGet( | |
| shape.size(), shape.data(), | |
| mlirIntegerTypeGet(ctx.get()->get(), width), encoding)); | |
| }, | |
| nb::arg("shape"), nb::arg("width"), | |
| nb::arg("context").none() = nb::none()); | |
| } | |
| }; | |
| struct PyTestTensorValue : PyConcreteValue<PyTestTensorValue> { | |
| static constexpr IsAFunctionTy isaFunction = | |
| mlirTypeIsAPythonTestTestTensorValue; | |
| static constexpr GetTypeIDFunctionTy getTypeIdFunction = | |
| mlirRankedTensorTypeGetTypeID; | |
| static constexpr const char *pyClassName = "TestTensorValue"; | |
| using PyConcreteValue::PyConcreteValue; | |
| static void bindDerived(ClassTy &c) { | |
| c.def("is_null", [](MlirValue &self) { return mlirValueIsNull(self); }); | |
| } | |
| }; | |
| class PyTestAttr : public PyConcreteAttribute<PyTestAttr> { | |
| public: | |
| static constexpr IsAFunctionTy isaFunction = | |
| mlirAttributeIsAPythonTestTestAttribute; | |
| static constexpr const char *pyClassName = "TestAttr"; | |
| using PyConcreteAttribute::PyConcreteAttribute; | |
| static constexpr GetTypeIDFunctionTy getTypeIdFunction = | |
| mlirPythonTestTestAttributeGetTypeID; | |
| static void bindDerived(ClassTy &c) { | |
| c.def_static( | |
| "get", | |
| [](DefaultingPyMlirContext context) { | |
| return PyTestAttr(context->getRef(), mlirPythonTestTestAttributeGet( | |
| context.get()->get())); | |
| }, | |
| nb::arg("context").none() = nb::none()); | |
| } | |
| }; | |
| } // namespace python_test | |
| } // namespace MLIR_BINDINGS_PYTHON_DOMAIN | |
| } // namespace python | |
| } // namespace mlir | |
| NB_MODULE(_mlirPythonTestNanobind, m) { | |
| using namespace mlir::python::MLIR_BINDINGS_PYTHON_DOMAIN; | |
| m.def( | |
| "register_python_test_dialect", | |
| [](DefaultingPyMlirContext context, bool load) { | |
| MlirDialectHandle pythonTestDialect = | |
| mlirGetDialectHandle__python_test__(); | |
| mlirDialectHandleRegisterDialect(pythonTestDialect, | |
| context.get()->get()); | |
| if (load) { | |
| mlirDialectHandleLoadDialect(pythonTestDialect, context.get()->get()); | |
| } | |
| }, | |
| nb::arg("context").none() = nb::none(), nb::arg("load") = true); | |
| m.def( | |
| "register_dialect", | |
| [](MlirDialectRegistry registry) { | |
| MlirDialectHandle pythonTestDialect = | |
| mlirGetDialectHandle__python_test__(); | |
| mlirDialectHandleInsertDialect(pythonTestDialect, registry); | |
| }, | |
| nb::arg("registry"), | |
| // clang-format off | |
| nb::sig("def register_dialect(registry: " MAKE_MLIR_PYTHON_QUALNAME("ir.DialectRegistry") ") -> None")); | |
| // clang-format on | |
| m.def( | |
| "test_diagnostics_with_errors_and_notes", | |
| [](DefaultingPyMlirContext ctx) { | |
| mlir::python::CollectDiagnosticsToStringScope handler(ctx.get()->get()); | |
| mlirPythonTestEmitDiagnosticWithNote(ctx.get()->get()); | |
| throw nb::value_error(handler.takeMessage().c_str()); | |
| }, | |
| nb::arg("context").none() = nb::none()); | |
| // Reproducer for the failed assertion `_PyType_LookupRef` triggered by | |
| // `NanobindAdaptors.h::from_python` type casters. | |
| // | |
| // Two overloads of the same function: one takes `MlirOperation`, the other | |
| // takes `MlirModule`. When called with an `ir.Module`: | |
| // | |
| // 1. nanobind tries overload 1 (`MlirOperation`). `from_python` calls | |
| // `mlirApiObjectToCapsule` (succeeds — `Module` has `_CAPIPtr`), then | |
| // `mlirPythonCapsuleToOperation`, whose `PyCapsule_GetPointer` fails on | |
| // the capsule-name mismatch and sets `PyErr_Occurred()`. | |
| // `from_python` returns false. | |
| // | |
| // If `PyErr` is still set and assertions are enabled: | |
| // 2. nanobind tries overload 2 (`MlirModule`). `from_python` calls | |
| // `mlirApiObjectToCapsule` --> `nanobind::getattr(obj, "_CAPIPtr")` --> | |
| // CPython's `_PyType_LookupRef` --> `assert(!PyErr_Occurred())` --> | |
| // `SIGABRT`. | |
| // | |
| // If `PyErr_Clear` is called after failed capsule conversion: | |
| // 2. `PyErr` is clear --> overload 2 succeeds --> returns "module". | |
| m.def( | |
| "take_module_or_operation", | |
| [](MlirOperation) { return std::string("operation"); }, nb::arg("arg")); | |
| m.def( | |
| "take_module_or_operation", | |
| [](MlirModule) { return std::string("module"); }, nb::arg("arg")); | |
| using namespace python_test; | |
| PyTestAttr::bind(m); | |
| PyTestType::bind(m); | |
| PyTestIntegerRankedTensorType::bind(m); | |
| PyTestTensorValue::bind(m); | |
| } |