Revert "[MLIR][Python] use FetchContent_Declare for nanobind and remove pybind (#161230)" (#162309)

This reverts commit 84a214856a.

This gives us more time to work out the alternative and also people to
migrate
This commit is contained in:
Maksim Levental
2025-10-07 12:30:10 -04:00
committed by GitHub
parent 278a99e8e9
commit 93097b2d47
18 changed files with 1082 additions and 121 deletions

View File

@@ -194,6 +194,10 @@ ml-dtypes==0.5.1 ; python_version < "3.13" \
--hash=sha256:d13755f8e8445b3870114e5b6240facaa7cb0c3361e54beba3e07fa912a6e12b \
--hash=sha256:fd918d4e6a4e0c110e2e05be7a7814d10dc1b95872accbf6512b80a109b71ae1
# via -r mlir/python/requirements.txt
nanobind==2.9.2 \
--hash=sha256:c37957ffd5eac7eda349cff3622ecd32e5ee1244ecc912c99b5bc8188bafd16e \
--hash=sha256:e7608472de99d375759814cab3e2c94aba3f9ec80e62cfef8ced495ca5c27d6e
# via -r mlir/python/requirements.txt
numpy==2.0.2 \
--hash=sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a \
--hash=sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195 \
@@ -295,6 +299,10 @@ pyasn1-modules==0.4.2 \
--hash=sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a \
--hash=sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6
# via google-auth
pybind11==2.13.6 \
--hash=sha256:237c41e29157b962835d356b370ededd57594a26d5894a795960f0047cb5caf5 \
--hash=sha256:ba6af10348c12b24e92fa086b39cfba0eff619b61ac77c406167d813b096d39a
# via -r mlir/python/requirements.txt
pyyaml==6.0.1 \
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \

View File

@@ -123,12 +123,12 @@ function(mlir_generate_type_stubs)
"IMPORT_PATHS;DEPENDS_TARGETS;OUTPUTS;DEPENDS_TARGET_SRC_DEPS"
${ARGN})
# for people installing a distro (e.g., pip install) of nanobind
# for people doing find_package(nanobind)
if(EXISTS ${nanobind_DIR}/../src/stubgen.py)
set(NB_STUBGEN "${nanobind_DIR}/../src/stubgen.py")
elseif(EXISTS ${nanobind_DIR}/../stubgen.py)
set(NB_STUBGEN "${nanobind_DIR}/../stubgen.py")
# for people using nanobind git source tree (e.g., FetchContent_Declare and FetchContent_MakeAvailable)
# for people using FetchContent_Declare and FetchContent_MakeAvailable
elseif(EXISTS ${nanobind_SOURCE_DIR}/src/stubgen.py)
set(NB_STUBGEN "${nanobind_SOURCE_DIR}/src/stubgen.py")
elseif(EXISTS ${nanobind_SOURCE_DIR}/stubgen.py)
@@ -226,10 +226,11 @@ endfunction()
# EMBED_CAPI_LINK_LIBS: Dependent CAPI libraries that this extension depends
# on. These will be collected for all extensions and put into an
# aggregate dylib that is linked against.
# PYTHON_BINDINGS_LIBRARY: Either pybind11 or nanobind.
function(declare_mlir_python_extension name)
cmake_parse_arguments(ARG
""
"ROOT_DIR;MODULE_NAME;ADD_TO_PARENT"
"ROOT_DIR;MODULE_NAME;ADD_TO_PARENT;PYTHON_BINDINGS_LIBRARY"
"SOURCES;PRIVATE_LINK_LIBS;EMBED_CAPI_LINK_LIBS"
${ARGN})
@@ -238,15 +239,20 @@ function(declare_mlir_python_extension name)
endif()
set(_install_destination "src/python/${name}")
if(NOT ARG_PYTHON_BINDINGS_LIBRARY)
set(ARG_PYTHON_BINDINGS_LIBRARY "pybind11")
endif()
add_library(${name} INTERFACE)
set_target_properties(${name} PROPERTIES
# Yes: Leading-lowercase property names are load bearing and the recommended
# way to do this: https://gitlab.kitware.com/cmake/cmake/-/issues/19261
EXPORT_PROPERTIES "mlir_python_SOURCES_TYPE;mlir_python_EXTENSION_MODULE_NAME;mlir_python_EMBED_CAPI_LINK_LIBS;mlir_python_DEPENDS"
EXPORT_PROPERTIES "mlir_python_SOURCES_TYPE;mlir_python_EXTENSION_MODULE_NAME;mlir_python_EMBED_CAPI_LINK_LIBS;mlir_python_DEPENDS;mlir_python_BINDINGS_LIBRARY"
mlir_python_SOURCES_TYPE extension
mlir_python_EXTENSION_MODULE_NAME "${ARG_MODULE_NAME}"
mlir_python_EMBED_CAPI_LINK_LIBS "${ARG_EMBED_CAPI_LINK_LIBS}"
mlir_python_DEPENDS ""
mlir_python_BINDINGS_LIBRARY "${ARG_PYTHON_BINDINGS_LIBRARY}"
)
# Set the interface source and link_libs properties of the target
@@ -335,12 +341,14 @@ function(add_mlir_python_modules name)
elseif(_source_type STREQUAL "extension")
# Native CPP extension.
get_target_property(_module_name ${sources_target} mlir_python_EXTENSION_MODULE_NAME)
get_target_property(_bindings_library ${sources_target} mlir_python_BINDINGS_LIBRARY)
# Transform relative source to based on root dir.
set(_extension_target "${modules_target}.extension.${_module_name}.dso")
add_mlir_python_extension(${_extension_target} "${_module_name}"
INSTALL_COMPONENT ${modules_target}
INSTALL_DIR "${ARG_INSTALL_PREFIX}/_mlir_libs"
OUTPUT_DIRECTORY "${ARG_ROOT_PREFIX}/_mlir_libs"
PYTHON_BINDINGS_LIBRARY ${_bindings_library}
LINK_LIBS PRIVATE
${sources_target}
${ARG_COMMON_CAPI_LINK_LIBS}
@@ -745,7 +753,7 @@ endfunction()
function(add_mlir_python_extension libname extname)
cmake_parse_arguments(ARG
""
"INSTALL_COMPONENT;INSTALL_DIR;OUTPUT_DIRECTORY"
"INSTALL_COMPONENT;INSTALL_DIR;OUTPUT_DIRECTORY;PYTHON_BINDINGS_LIBRARY"
"SOURCES;LINK_LIBS"
${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
@@ -753,7 +761,7 @@ function(add_mlir_python_extension libname extname)
endif()
# The extension itself must be compiled with RTTI and exceptions enabled.
# Also, some warning classes triggered by nanobind are disabled.
# Also, some warning classes triggered by pybind11 are disabled.
set(eh_rtti_enable)
if (MSVC)
set(eh_rtti_enable /EHsc /GR)
@@ -761,53 +769,62 @@ function(add_mlir_python_extension libname extname)
set(eh_rtti_enable -frtti -fexceptions)
endif ()
nanobind_add_module(${libname}
NB_DOMAIN ${MLIR_BINDINGS_PYTHON_NB_DOMAIN}
FREE_THREADED
${ARG_SOURCES}
)
if (NOT MLIR_DISABLE_CONFIGURE_PYTHON_DEV_PACKAGES
AND (LLVM_COMPILER_IS_GCC_COMPATIBLE OR CLANG_CL))
# Avoid some warnings from upstream nanobind.
# If a superproject set MLIR_DISABLE_CONFIGURE_PYTHON_DEV_PACKAGES, let
# the super project handle compile options as it wishes.
get_property(NB_LIBRARY_TARGET_NAME TARGET ${libname} PROPERTY LINK_LIBRARIES)
target_compile_options(${NB_LIBRARY_TARGET_NAME}
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-c++98-compat-extra-semi
-Wno-cast-qual
-Wno-covered-switch-default
-Wno-deprecated-literal-operator
-Wno-nested-anon-types
-Wno-unused-parameter
-Wno-zero-length-array
${eh_rtti_enable})
target_compile_options(${libname}
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-c++98-compat-extra-semi
-Wno-cast-qual
-Wno-covered-switch-default
-Wno-deprecated-literal-operator
-Wno-nested-anon-types
-Wno-unused-parameter
-Wno-zero-length-array
${eh_rtti_enable})
endif()
if(APPLE)
# NanobindAdaptors.h uses PyClassMethod_New to build `pure_subclass`es but nanobind
# doesn't declare this API as undefined in its linker flags. So we need to declare it as such
# for downstream users that do not do something like `-undefined dynamic_lookup`.
# Same for the rest.
target_link_options(${libname} PUBLIC
"LINKER:-U,_PyClassMethod_New"
"LINKER:-U,_PyCode_Addr2Location"
"LINKER:-U,_PyFrame_GetLasti"
# The actual extension library produces a shared-object or DLL and has
# sources that must be compiled in accordance with pybind11 needs (RTTI and
# exceptions).
if(NOT DEFINED ARG_PYTHON_BINDINGS_LIBRARY OR ARG_PYTHON_BINDINGS_LIBRARY STREQUAL "pybind11")
pybind11_add_module(${libname}
${ARG_SOURCES}
)
elseif(ARG_PYTHON_BINDINGS_LIBRARY STREQUAL "nanobind")
nanobind_add_module(${libname}
NB_DOMAIN ${MLIR_BINDINGS_PYTHON_NB_DOMAIN}
FREE_THREADED
${ARG_SOURCES}
)
if (NOT MLIR_DISABLE_CONFIGURE_PYTHON_DEV_PACKAGES
AND (LLVM_COMPILER_IS_GCC_COMPATIBLE OR CLANG_CL))
# Avoid some warnings from upstream nanobind.
# If a superproject set MLIR_DISABLE_CONFIGURE_PYTHON_DEV_PACKAGES, let
# the super project handle compile options as it wishes.
get_property(NB_LIBRARY_TARGET_NAME TARGET ${libname} PROPERTY LINK_LIBRARIES)
target_compile_options(${NB_LIBRARY_TARGET_NAME}
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-c++98-compat-extra-semi
-Wno-cast-qual
-Wno-covered-switch-default
-Wno-deprecated-literal-operator
-Wno-nested-anon-types
-Wno-unused-parameter
-Wno-zero-length-array
${eh_rtti_enable})
target_compile_options(${libname}
PRIVATE
-Wall -Wextra -Wpedantic
-Wno-c++98-compat-extra-semi
-Wno-cast-qual
-Wno-covered-switch-default
-Wno-deprecated-literal-operator
-Wno-nested-anon-types
-Wno-unused-parameter
-Wno-zero-length-array
${eh_rtti_enable})
endif()
if(APPLE)
# NanobindAdaptors.h uses PyClassMethod_New to build `pure_subclass`es but nanobind
# doesn't declare this API as undefined in its linker flags. So we need to declare it as such
# for downstream users that do not do something like `-undefined dynamic_lookup`.
# Same for the rest.
target_link_options(${libname} PUBLIC
"LINKER:-U,_PyClassMethod_New"
"LINKER:-U,_PyCode_Addr2Location"
"LINKER:-U,_PyFrame_GetLasti"
)
endif()
endif()
target_compile_options(${libname} PRIVATE ${eh_rtti_enable})
@@ -845,11 +862,11 @@ function(add_mlir_python_extension libname extname)
if(WIN32)
# On Windows, pyconfig.h (and by extension python.h) hardcode the version of the
# python library which will be used for linkage depending on the flavor of the build.
# nanobind has a workaround which depends on the definition of Py_DEBUG (if Py_DEBUG
# is not passed in as a compile definition, nanobind undefs _DEBUG when including
# pybind11 has a workaround which depends on the definition of Py_DEBUG (if Py_DEBUG
# is not passed in as a compile definition, pybind11 undefs _DEBUG when including
# python.h, so that the release python library would be used).
# Since mlir uses nanobind, we can leverage their workaround by never directly
# pyconfig.h or python.h and instead relying on the nanobind headers to include the
# Since mlir uses pybind11, we can leverage their workaround by never directly
# pyconfig.h or python.h and instead relying on the pybind11 headers to include the
# necessary python headers. This results in mlir always linking against the
# release python library via the (undocumented) cmake property Python3_LIBRARY_RELEASE.
target_link_libraries(${libname} PRIVATE ${Python3_LIBRARY_RELEASE})

View File

@@ -46,20 +46,81 @@ macro(mlir_configure_python_dev_packages)
message(STATUS "Found python include dirs: ${Python3_INCLUDE_DIRS}")
message(STATUS "Found python libraries: ${Python3_LIBRARIES}")
message(STATUS "Found numpy v${Python3_NumPy_VERSION}: ${Python3_NumPy_INCLUDE_DIRS}")
message(STATUS "Python extension suffix for modules: '${Python3_SOABI}'")
if(nanobind_DIR)
message(STATUS "Using explicit nanobind cmake directory: ${nanobind_DIR} (-Dnanobind_DIR to change)")
find_package(nanobind 2.9 CONFIG REQUIRED)
else()
include(FetchContent)
FetchContent_Declare(
nanobind
GIT_REPOSITORY https://github.com/wjakob/nanobind.git
GIT_TAG v2.9.0
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(nanobind)
endif()
message(STATUS "Found nanobind: ${NB_DIR}")
mlir_detect_pybind11_install()
find_package(pybind11 2.10 CONFIG REQUIRED)
message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIR}")
message(STATUS "Python prefix = '${PYTHON_MODULE_PREFIX}', "
"suffix = '${PYTHON_MODULE_SUFFIX}', "
"extension = '${PYTHON_MODULE_EXTENSION}")
mlir_detect_nanobind_install()
find_package(nanobind 2.9 CONFIG REQUIRED)
message(STATUS "Found nanobind v${nanobind_VERSION}: ${nanobind_INCLUDE_DIR}")
message(STATUS "Python prefix = '${PYTHON_MODULE_PREFIX}', "
"suffix = '${PYTHON_MODULE_SUFFIX}', "
"extension = '${PYTHON_MODULE_EXTENSION}")
endif()
endmacro()
# Detects a pybind11 package installed in the current python environment
# and sets variables to allow it to be found. This allows pybind11 to be
# installed via pip, which typically yields a much more recent version than
# the OS install, which will be available otherwise.
function(mlir_detect_pybind11_install)
if(pybind11_DIR)
message(STATUS "Using explicit pybind11 cmake directory: ${pybind11_DIR} (-Dpybind11_DIR to change)")
else()
message(STATUS "Checking for pybind11 in python path...")
execute_process(
COMMAND "${Python3_EXECUTABLE}"
-c "import pybind11;print(pybind11.get_cmake_dir(), end='')"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE STATUS
OUTPUT_VARIABLE PACKAGE_DIR
ERROR_QUIET)
if(NOT STATUS EQUAL "0")
message(STATUS "not found (install via 'pip install pybind11' or set pybind11_DIR)")
return()
endif()
message(STATUS "found (${PACKAGE_DIR})")
set(pybind11_DIR "${PACKAGE_DIR}" PARENT_SCOPE)
endif()
endfunction()
# Detects a nanobind package installed in the current python environment
# and sets variables to allow it to be found. This allows nanobind to be
# installed via pip, which typically yields a much more recent version than
# the OS install, which will be available otherwise.
function(mlir_detect_nanobind_install)
if(nanobind_DIR)
message(STATUS "Using explicit nanobind cmake directory: ${nanobind_DIR} (-Dnanobind_DIR to change)")
else()
message(STATUS "Checking for nanobind in python path...")
execute_process(
COMMAND "${Python3_EXECUTABLE}"
-c "import nanobind;print(nanobind.cmake_dir(), end='')"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE STATUS
OUTPUT_VARIABLE PACKAGE_DIR
ERROR_QUIET)
if(NOT STATUS EQUAL "0")
message(STATUS "not found (install via 'pip install nanobind' or set nanobind_DIR)")
return()
endif()
message(STATUS "found (${PACKAGE_DIR})")
set(nanobind_DIR "${PACKAGE_DIR}" PARENT_SCOPE)
execute_process(
COMMAND "${Python3_EXECUTABLE}"
-c "import nanobind;print(nanobind.include_dir(), end='')"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE STATUS
OUTPUT_VARIABLE PACKAGE_DIR
ERROR_QUIET)
if(NOT STATUS EQUAL "0")
message(STATUS "not found (install via 'pip install nanobind' or set nanobind_DIR)")
return()
endif()
set(nanobind_INCLUDE_DIR "${PACKAGE_DIR}" PARENT_SCOPE)
endif()
endfunction()

View File

@@ -16,7 +16,7 @@ corresponding `linalg.generic` IR for the composition.
## Basic usage
The tool is bundled with the MLIR Python bindings. To use from the CMake build
tree, MLIR must be built with Python bindings enabled
tree, MLIR must be build with Python bindings enabled
(`-DMLIR_ENABLE_BINDINGS_PYTHON=ON`). Then add the `python` directory in the
build tree to your `PYTHONPATH` environment variable (i.e. `export
PYTHONPATH=$PWD/build/tools/mlir/python_packages/mlir_core`). Optionally, use an
@@ -24,7 +24,7 @@ installed MLIR package, if available, to avoid building.
```shell
# Dump the `core_named_ops.py` module as YAML.
python -m mlir.dialects.linalg.opdsl.dump_oplib.ops.core_named_ops
python -m mlir.dialects.linalg.opdsl.dump_oplib .ops.core_named_ops
```
Alternatively, run the `$PWD/build/bin/update_core_linalg_named_ops.sh` script,

View File

@@ -23,7 +23,9 @@ Discussions = "https://discourse.llvm.org/"
[build-system]
requires = [
"scikit-build-core>=0.10.7",
"typing_extensions>=4.12.2"
"typing_extensions>=4.12.2",
"nanobind>=2.9, <3.0",
"pybind11>=2.10.0, <=2.13.6",
]
build-backend = "scikit_build_core.build"

View File

@@ -16,10 +16,27 @@ declare_mlir_dialect_python_bindings(
ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/mlir_standalone"
TD_FILE dialects/StandaloneOps.td
SOURCES
dialects/standalone_pybind11.py
dialects/standalone_nanobind.py
_mlir_libs/_standaloneDialectsNanobind/py.typed
DIALECT_NAME standalone)
declare_mlir_python_extension(StandalonePythonSources.Pybind11Extension
MODULE_NAME _standaloneDialectsPybind11
ADD_TO_PARENT StandalonePythonSources
SOURCES
StandaloneExtensionPybind11.cpp
PRIVATE_LINK_LIBS
LLVMSupport
EMBED_CAPI_LINK_LIBS
MLIRCAPIIR
MLIRCAPIArith
MLIRCAPITransforms
StandaloneCAPI
PYTHON_BINDINGS_LIBRARY pybind11
)
declare_mlir_python_extension(StandalonePythonSources.NanobindExtension
MODULE_NAME _standaloneDialectsNanobind
ADD_TO_PARENT StandalonePythonSources
@@ -32,6 +49,7 @@ declare_mlir_python_extension(StandalonePythonSources.NanobindExtension
MLIRCAPIArith
MLIRCAPITransforms
StandaloneCAPI
PYTHON_BINDINGS_LIBRARY nanobind
)

View File

@@ -0,0 +1,38 @@
//===- StandaloneExtensionPybind11.cpp - Extension module -----------------===//
//
// This is the pybind11 version of the example module. There is also a nanobind
// example in StandaloneExtensionNanobind.cpp.
//
// 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
//
//===----------------------------------------------------------------------===//
#include "Standalone-c/Dialects.h"
#include "mlir-c/Dialect/Arith.h"
#include "mlir/Bindings/Python/PybindAdaptors.h"
using namespace mlir::python::adaptors;
PYBIND11_MODULE(_standaloneDialectsPybind11, m) {
//===--------------------------------------------------------------------===//
// standalone dialect
//===--------------------------------------------------------------------===//
auto standaloneM = m.def_submodule("standalone");
standaloneM.def(
"register_dialects",
[](MlirContext context, bool load) {
MlirDialectHandle arithHandle = mlirGetDialectHandle__arith__();
MlirDialectHandle standaloneHandle =
mlirGetDialectHandle__standalone__();
mlirDialectHandleRegisterDialect(arithHandle, context);
mlirDialectHandleRegisterDialect(standaloneHandle, context);
if (load) {
mlirDialectHandleLoadDialect(arithHandle, context);
mlirDialectHandleRegisterDialect(standaloneHandle, context);
}
},
py::arg("context") = py::none(), py::arg("load") = true);
}

View File

@@ -0,0 +1,6 @@
# 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
from ._standalone_ops_gen import *
from .._mlir_libs._standaloneDialectsPybind11.standalone import *

View File

@@ -1,7 +1,16 @@
# RUN: %python %s pybind11 | FileCheck %s
# RUN: %python %s nanobind | FileCheck %s
import sys
from mlir_standalone.ir import *
from mlir_standalone.dialects import standalone_nanobind as standalone_d
if sys.argv[1] == "pybind11":
from mlir_standalone.dialects import standalone_pybind11 as standalone_d
elif sys.argv[1] == "nanobind":
from mlir_standalone.dialects import standalone_nanobind as standalone_d
else:
raise ValueError("Expected either pybind11 or nanobind as arguments")
with Context():
standalone_d.register_dialects()

View File

@@ -0,0 +1,616 @@
//===- PybindAdaptors.h - Interop with MLIR APIs via pybind11 -------------===//
//
// 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 file contains adaptors for clients of the core MLIR Python APIs to
// interop via MLIR CAPI types, using pybind11. The facilities here do not
// depend on implementation details of the MLIR Python API and do not introduce
// C++-level dependencies with it (requiring only Python and CAPI-level
// dependencies).
//
// It is encouraged to be used both in-tree and out-of-tree. For in-tree use
// cases, it should be used for dialect implementations (versus relying on
// Pybind-based internals of the core libraries).
//===----------------------------------------------------------------------===//
#ifndef MLIR_BINDINGS_PYTHON_PYBINDADAPTORS_H
#define MLIR_BINDINGS_PYTHON_PYBINDADAPTORS_H
#include <pybind11/functional.h>
#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>
#include <pybind11/stl.h>
#include "mlir-c/Bindings/Python/Interop.h"
#include "mlir-c/Diagnostics.h"
#include "mlir-c/IR.h"
#include "llvm/ADT/Twine.h"
namespace py = pybind11;
using namespace py::literals;
// Raw CAPI type casters need to be declared before use, so always include them
// first.
namespace pybind11 {
namespace detail {
/// Helper to convert a presumed MLIR API object to a capsule, accepting either
/// an explicit Capsule (which can happen when two C APIs are communicating
/// directly via Python) or indirectly by querying the MLIR_PYTHON_CAPI_PTR_ATTR
/// attribute (through which supported MLIR Python API objects export their
/// contained API pointer as a capsule). Throws a type error if the object is
/// neither. This is intended to be used from type casters, which are invoked
/// with a raw handle (unowned). The returned object's lifetime may not extend
/// beyond the apiObject handle without explicitly having its refcount increased
/// (i.e. on return).
static py::object mlirApiObjectToCapsule(py::handle apiObject) {
if (PyCapsule_CheckExact(apiObject.ptr()))
return py::reinterpret_borrow<py::object>(apiObject);
if (!py::hasattr(apiObject, MLIR_PYTHON_CAPI_PTR_ATTR)) {
auto repr = py::repr(apiObject).cast<std::string>();
throw py::type_error(
(llvm::Twine("Expected an MLIR object (got ") + repr + ").").str());
}
return apiObject.attr(MLIR_PYTHON_CAPI_PTR_ATTR);
}
// Note: Currently all of the following support cast from py::object to the
// Mlir* C-API type, but only a few light-weight, context-bound ones
// implicitly cast the other way because the use case has not yet emerged and
// ownership is unclear.
/// Casts object <-> MlirAffineMap.
template <>
struct type_caster<MlirAffineMap> {
PYBIND11_TYPE_CASTER(MlirAffineMap, _("MlirAffineMap"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToAffineMap(capsule.ptr());
if (mlirAffineMapIsNull(value)) {
return false;
}
return !mlirAffineMapIsNull(value);
}
static handle cast(MlirAffineMap v, return_value_policy, handle) {
py::object capsule =
py::reinterpret_steal<py::object>(mlirPythonAffineMapToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("AffineMap")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
}
};
/// Casts object <-> MlirAttribute.
template <>
struct type_caster<MlirAttribute> {
PYBIND11_TYPE_CASTER(MlirAttribute, _("MlirAttribute"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToAttribute(capsule.ptr());
return !mlirAttributeIsNull(value);
}
static handle cast(MlirAttribute v, return_value_policy, handle) {
py::object capsule =
py::reinterpret_steal<py::object>(mlirPythonAttributeToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Attribute")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.attr(MLIR_PYTHON_MAYBE_DOWNCAST_ATTR)()
.release();
}
};
/// Casts object -> MlirBlock.
template <>
struct type_caster<MlirBlock> {
PYBIND11_TYPE_CASTER(MlirBlock, _("MlirBlock"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToBlock(capsule.ptr());
return !mlirBlockIsNull(value);
}
};
/// Casts object -> MlirContext.
template <>
struct type_caster<MlirContext> {
PYBIND11_TYPE_CASTER(MlirContext, _("MlirContext"));
bool load(handle src, bool) {
if (src.is_none()) {
// Gets the current thread-bound context.
// TODO: This raises an error of "No current context" currently.
// Update the implementation to pretty-print the helpful error that the
// core implementations print in this case.
src = py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Context")
.attr("current");
}
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToContext(capsule.ptr());
return !mlirContextIsNull(value);
}
};
/// Casts object <-> MlirDialectRegistry.
template <>
struct type_caster<MlirDialectRegistry> {
PYBIND11_TYPE_CASTER(MlirDialectRegistry, _("MlirDialectRegistry"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToDialectRegistry(capsule.ptr());
return !mlirDialectRegistryIsNull(value);
}
static handle cast(MlirDialectRegistry v, return_value_policy, handle) {
py::object capsule = py::reinterpret_steal<py::object>(
mlirPythonDialectRegistryToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("DialectRegistry")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
}
};
/// Casts object <-> MlirLocation.
template <>
struct type_caster<MlirLocation> {
PYBIND11_TYPE_CASTER(MlirLocation, _("MlirLocation"));
bool load(handle src, bool) {
if (src.is_none()) {
// Gets the current thread-bound context.
src = py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Location")
.attr("current");
}
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToLocation(capsule.ptr());
return !mlirLocationIsNull(value);
}
static handle cast(MlirLocation v, return_value_policy, handle) {
py::object capsule =
py::reinterpret_steal<py::object>(mlirPythonLocationToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Location")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
}
};
/// Casts object <-> MlirModule.
template <>
struct type_caster<MlirModule> {
PYBIND11_TYPE_CASTER(MlirModule, _("MlirModule"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToModule(capsule.ptr());
return !mlirModuleIsNull(value);
}
static handle cast(MlirModule v, return_value_policy, handle) {
py::object capsule =
py::reinterpret_steal<py::object>(mlirPythonModuleToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Module")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
};
};
/// Casts object <-> MlirFrozenRewritePatternSet.
template <>
struct type_caster<MlirFrozenRewritePatternSet> {
PYBIND11_TYPE_CASTER(MlirFrozenRewritePatternSet,
_("MlirFrozenRewritePatternSet"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule.ptr());
return value.ptr != nullptr;
}
static handle cast(MlirFrozenRewritePatternSet v, return_value_policy,
handle) {
py::object capsule = py::reinterpret_steal<py::object>(
mlirPythonFrozenRewritePatternSetToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("rewrite"))
.attr("FrozenRewritePatternSet")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
};
};
/// Casts object <-> MlirOperation.
template <>
struct type_caster<MlirOperation> {
PYBIND11_TYPE_CASTER(MlirOperation, _("MlirOperation"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToOperation(capsule.ptr());
return !mlirOperationIsNull(value);
}
static handle cast(MlirOperation v, return_value_policy, handle) {
if (v.ptr == nullptr)
return py::none();
py::object capsule =
py::reinterpret_steal<py::object>(mlirPythonOperationToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Operation")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
};
};
/// Casts object <-> MlirValue.
template <>
struct type_caster<MlirValue> {
PYBIND11_TYPE_CASTER(MlirValue, _("MlirValue"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToValue(capsule.ptr());
return !mlirValueIsNull(value);
}
static handle cast(MlirValue v, return_value_policy, handle) {
if (v.ptr == nullptr)
return py::none();
py::object capsule =
py::reinterpret_steal<py::object>(mlirPythonValueToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Value")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.attr(MLIR_PYTHON_MAYBE_DOWNCAST_ATTR)()
.release();
};
};
/// Casts object -> MlirPassManager.
template <>
struct type_caster<MlirPassManager> {
PYBIND11_TYPE_CASTER(MlirPassManager, _("MlirPassManager"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToPassManager(capsule.ptr());
return !mlirPassManagerIsNull(value);
}
};
/// Casts object <-> MlirTypeID.
template <>
struct type_caster<MlirTypeID> {
PYBIND11_TYPE_CASTER(MlirTypeID, _("MlirTypeID"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToTypeID(capsule.ptr());
return !mlirTypeIDIsNull(value);
}
static handle cast(MlirTypeID v, return_value_policy, handle) {
if (v.ptr == nullptr)
return py::none();
py::object capsule =
py::reinterpret_steal<py::object>(mlirPythonTypeIDToCapsule(v));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("TypeID")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
};
};
/// Casts object <-> MlirType.
template <>
struct type_caster<MlirType> {
PYBIND11_TYPE_CASTER(MlirType, _("MlirType"));
bool load(handle src, bool) {
py::object capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToType(capsule.ptr());
return !mlirTypeIsNull(value);
}
static handle cast(MlirType t, return_value_policy, handle) {
py::object capsule =
py::reinterpret_steal<py::object>(mlirPythonTypeToCapsule(t));
return py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Type")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.attr(MLIR_PYTHON_MAYBE_DOWNCAST_ATTR)()
.release();
}
};
} // namespace detail
} // namespace pybind11
namespace mlir {
namespace python {
namespace adaptors {
/// Provides a facility like py::class_ for defining a new class in a scope,
/// but this allows extension of an arbitrary Python class, defining methods
/// on it is a similar way. Classes defined in this way are very similar to
/// if defined in Python in the usual way but use Pybind11 machinery to do
/// it. These are not "real" Pybind11 classes but pure Python classes with no
/// relation to a concrete C++ class.
///
/// Derived from a discussion upstream:
/// https://github.com/pybind/pybind11/issues/1193
/// (plus a fair amount of extra curricular poking)
/// TODO: If this proves useful, see about including it in pybind11.
class pure_subclass {
public:
pure_subclass(py::handle scope, const char *derivedClassName,
const py::object &superClass) {
py::object pyType =
py::reinterpret_borrow<py::object>((PyObject *)&PyType_Type);
py::object metaclass = pyType(superClass);
py::dict attributes;
thisClass =
metaclass(derivedClassName, py::make_tuple(superClass), attributes);
scope.attr(derivedClassName) = thisClass;
}
template <typename Func, typename... Extra>
pure_subclass &def(const char *name, Func &&f, const Extra &...extra) {
py::cpp_function cf(
std::forward<Func>(f), py::name(name), py::is_method(thisClass),
py::sibling(py::getattr(thisClass, name, py::none())), extra...);
thisClass.attr(cf.name()) = cf;
return *this;
}
template <typename Func, typename... Extra>
pure_subclass &def_property_readonly(const char *name, Func &&f,
const Extra &...extra) {
py::cpp_function cf(
std::forward<Func>(f), py::name(name), py::is_method(thisClass),
py::sibling(py::getattr(thisClass, name, py::none())), extra...);
auto builtinProperty =
py::reinterpret_borrow<py::object>((PyObject *)&PyProperty_Type);
thisClass.attr(name) = builtinProperty(cf);
return *this;
}
template <typename Func, typename... Extra>
pure_subclass &def_staticmethod(const char *name, Func &&f,
const Extra &...extra) {
static_assert(!std::is_member_function_pointer<Func>::value,
"def_staticmethod(...) called with a non-static member "
"function pointer");
py::cpp_function cf(std::forward<Func>(f), py::name(name),
py::scope(thisClass), extra...);
thisClass.attr(cf.name()) = py::staticmethod(cf);
return *this;
}
template <typename Func, typename... Extra>
pure_subclass &def_classmethod(const char *name, Func &&f,
const Extra &...extra) {
static_assert(!std::is_member_function_pointer<Func>::value,
"def_classmethod(...) called with a non-static member "
"function pointer");
py::cpp_function cf(std::forward<Func>(f), py::name(name),
py::scope(thisClass), extra...);
thisClass.attr(cf.name()) =
py::reinterpret_borrow<py::object>(PyClassMethod_New(cf.ptr()));
return *this;
}
py::object get_class() const { return thisClass; }
protected:
py::object superClass;
py::object thisClass;
};
/// Creates a custom subclass of mlir.ir.Attribute, implementing a casting
/// constructor and type checking methods.
class mlir_attribute_subclass : public pure_subclass {
public:
using IsAFunctionTy = bool (*)(MlirAttribute);
using GetTypeIDFunctionTy = MlirTypeID (*)();
/// Subclasses by looking up the super-class dynamically.
mlir_attribute_subclass(py::handle scope, const char *attrClassName,
IsAFunctionTy isaFunction,
GetTypeIDFunctionTy getTypeIDFunction = nullptr)
: mlir_attribute_subclass(
scope, attrClassName, isaFunction,
py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("Attribute"),
getTypeIDFunction) {}
/// Subclasses with a provided mlir.ir.Attribute super-class. This must
/// be used if the subclass is being defined in the same extension module
/// as the mlir.ir class (otherwise, it will trigger a recursive
/// initialization).
mlir_attribute_subclass(py::handle scope, const char *typeClassName,
IsAFunctionTy isaFunction, const py::object &superCls,
GetTypeIDFunctionTy getTypeIDFunction = nullptr)
: pure_subclass(scope, typeClassName, superCls) {
// Casting constructor. Note that it hard, if not impossible, to properly
// call chain to parent `__init__` in pybind11 due to its special handling
// for init functions that don't have a fully constructed self-reference,
// which makes it impossible to forward it to `__init__` of a superclass.
// Instead, provide a custom `__new__` and call that of a superclass, which
// eventually calls `__init__` of the superclass. Since attribute subclasses
// have no additional members, we can just return the instance thus created
// without amending it.
std::string captureTypeName(
typeClassName); // As string in case if typeClassName is not static.
py::cpp_function newCf(
[superCls, isaFunction, captureTypeName](py::object cls,
py::object otherAttribute) {
MlirAttribute rawAttribute = py::cast<MlirAttribute>(otherAttribute);
if (!isaFunction(rawAttribute)) {
auto origRepr = py::repr(otherAttribute).cast<std::string>();
throw std::invalid_argument(
(llvm::Twine("Cannot cast attribute to ") + captureTypeName +
" (from " + origRepr + ")")
.str());
}
py::object self = superCls.attr("__new__")(cls, otherAttribute);
return self;
},
py::name("__new__"), py::arg("cls"), py::arg("cast_from_attr"));
thisClass.attr("__new__") = newCf;
// 'isinstance' method.
def_staticmethod(
"isinstance",
[isaFunction](MlirAttribute other) { return isaFunction(other); },
py::arg("other_attribute"));
def("__repr__", [superCls, captureTypeName](py::object self) {
return py::repr(superCls(self))
.attr("replace")(superCls.attr("__name__"), captureTypeName);
});
if (getTypeIDFunction) {
def_staticmethod("get_static_typeid",
[getTypeIDFunction]() { return getTypeIDFunction(); });
py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr(MLIR_PYTHON_CAPI_TYPE_CASTER_REGISTER_ATTR)(
getTypeIDFunction())(pybind11::cpp_function(
[thisClass = thisClass](const py::object &mlirAttribute) {
return thisClass(mlirAttribute);
}));
}
}
};
/// Creates a custom subclass of mlir.ir.Type, implementing a casting
/// constructor and type checking methods.
class mlir_type_subclass : public pure_subclass {
public:
using IsAFunctionTy = bool (*)(MlirType);
using GetTypeIDFunctionTy = MlirTypeID (*)();
/// Subclasses by looking up the super-class dynamically.
mlir_type_subclass(py::handle scope, const char *typeClassName,
IsAFunctionTy isaFunction,
GetTypeIDFunctionTy getTypeIDFunction = nullptr)
: mlir_type_subclass(
scope, typeClassName, isaFunction,
py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir")).attr("Type"),
getTypeIDFunction) {}
/// Subclasses with a provided mlir.ir.Type super-class. This must
/// be used if the subclass is being defined in the same extension module
/// as the mlir.ir class (otherwise, it will trigger a recursive
/// initialization).
mlir_type_subclass(py::handle scope, const char *typeClassName,
IsAFunctionTy isaFunction, const py::object &superCls,
GetTypeIDFunctionTy getTypeIDFunction = nullptr)
: pure_subclass(scope, typeClassName, superCls) {
// Casting constructor. Note that it hard, if not impossible, to properly
// call chain to parent `__init__` in pybind11 due to its special handling
// for init functions that don't have a fully constructed self-reference,
// which makes it impossible to forward it to `__init__` of a superclass.
// Instead, provide a custom `__new__` and call that of a superclass, which
// eventually calls `__init__` of the superclass. Since attribute subclasses
// have no additional members, we can just return the instance thus created
// without amending it.
std::string captureTypeName(
typeClassName); // As string in case if typeClassName is not static.
py::cpp_function newCf(
[superCls, isaFunction, captureTypeName](py::object cls,
py::object otherType) {
MlirType rawType = py::cast<MlirType>(otherType);
if (!isaFunction(rawType)) {
auto origRepr = py::repr(otherType).cast<std::string>();
throw std::invalid_argument((llvm::Twine("Cannot cast type to ") +
captureTypeName + " (from " +
origRepr + ")")
.str());
}
py::object self = superCls.attr("__new__")(cls, otherType);
return self;
},
py::name("__new__"), py::arg("cls"), py::arg("cast_from_type"));
thisClass.attr("__new__") = newCf;
// 'isinstance' method.
def_staticmethod(
"isinstance",
[isaFunction](MlirType other) { return isaFunction(other); },
py::arg("other_type"));
def("__repr__", [superCls, captureTypeName](py::object self) {
return py::repr(superCls(self))
.attr("replace")(superCls.attr("__name__"), captureTypeName);
});
if (getTypeIDFunction) {
// 'get_static_typeid' method.
// This is modeled as a static method instead of a static property because
// `def_property_readonly_static` is not available in `pure_subclass` and
// we do not want to introduce the complexity that pybind uses to
// implement it.
def_staticmethod("get_static_typeid",
[getTypeIDFunction]() { return getTypeIDFunction(); });
py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr(MLIR_PYTHON_CAPI_TYPE_CASTER_REGISTER_ATTR)(
getTypeIDFunction())(pybind11::cpp_function(
[thisClass = thisClass](const py::object &mlirType) {
return thisClass(mlirType);
}));
}
}
};
/// Creates a custom subclass of mlir.ir.Value, implementing a casting
/// constructor and type checking methods.
class mlir_value_subclass : public pure_subclass {
public:
using IsAFunctionTy = bool (*)(MlirValue);
/// Subclasses by looking up the super-class dynamically.
mlir_value_subclass(py::handle scope, const char *valueClassName,
IsAFunctionTy isaFunction)
: mlir_value_subclass(
scope, valueClassName, isaFunction,
py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir")).attr("Value")) {
}
/// Subclasses with a provided mlir.ir.Value super-class. This must
/// be used if the subclass is being defined in the same extension module
/// as the mlir.ir class (otherwise, it will trigger a recursive
/// initialization).
mlir_value_subclass(py::handle scope, const char *valueClassName,
IsAFunctionTy isaFunction, const py::object &superCls)
: pure_subclass(scope, valueClassName, superCls) {
// Casting constructor. Note that it hard, if not impossible, to properly
// call chain to parent `__init__` in pybind11 due to its special handling
// for init functions that don't have a fully constructed self-reference,
// which makes it impossible to forward it to `__init__` of a superclass.
// Instead, provide a custom `__new__` and call that of a superclass, which
// eventually calls `__init__` of the superclass. Since attribute subclasses
// have no additional members, we can just return the instance thus created
// without amending it.
std::string captureValueName(
valueClassName); // As string in case if valueClassName is not static.
py::cpp_function newCf(
[superCls, isaFunction, captureValueName](py::object cls,
py::object otherValue) {
MlirValue rawValue = py::cast<MlirValue>(otherValue);
if (!isaFunction(rawValue)) {
auto origRepr = py::repr(otherValue).cast<std::string>();
throw std::invalid_argument((llvm::Twine("Cannot cast value to ") +
captureValueName + " (from " +
origRepr + ")")
.str());
}
py::object self = superCls.attr("__new__")(cls, otherValue);
return self;
},
py::name("__new__"), py::arg("cls"), py::arg("cast_from_value"));
thisClass.attr("__new__") = newCf;
// 'isinstance' method.
def_staticmethod(
"isinstance",
[isaFunction](MlirValue other) { return isaFunction(other); },
py::arg("other_value"));
}
};
} // namespace adaptors
} // namespace python
} // namespace mlir
#endif // MLIR_BINDINGS_PYTHON_PYBINDADAPTORS_H

View File

@@ -440,11 +440,11 @@ declare_mlir_dialect_python_bindings(
DIALECT_NAME smt)
declare_mlir_dialect_python_bindings(
ADD_TO_PARENT MLIRPythonSources.Dialects
ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/mlir"
TD_FILE dialects/SPIRVOps.td
SOURCES dialects/spirv.py
DIALECT_NAME spirv)
ADD_TO_PARENT MLIRPythonSources.Dialects
ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/mlir"
TD_FILE dialects/SPIRVOps.td
SOURCES dialects/spirv.py
DIALECT_NAME spirv)
declare_mlir_dialect_python_bindings(
ADD_TO_PARENT MLIRPythonSources.Dialects
@@ -501,6 +501,7 @@ declare_mlir_python_extension(MLIRPythonExtension.Core
MODULE_NAME _mlir
ADD_TO_PARENT MLIRPythonSources.Core
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
MainModule.cpp
IRAffine.cpp
@@ -539,6 +540,7 @@ declare_mlir_python_extension(MLIRPythonExtension.Core
declare_mlir_python_extension(MLIRPythonExtension.RegisterEverything
MODULE_NAME _mlirRegisterEverything
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
RegisterEverything.cpp
PRIVATE_LINK_LIBS
@@ -549,10 +551,11 @@ declare_mlir_python_extension(MLIRPythonExtension.RegisterEverything
MLIRCAPIRegisterEverything
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.Linalg.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.Linalg.Pybind
MODULE_NAME _mlirDialectsLinalg
ADD_TO_PARENT MLIRPythonSources.Dialects.linalg
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectLinalg.cpp
PRIVATE_LINK_LIBS
@@ -562,10 +565,11 @@ declare_mlir_python_extension(MLIRPythonExtension.Dialects.Linalg.Nanobind
MLIRCAPILinalg
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.GPU.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.GPU.Pybind
MODULE_NAME _mlirDialectsGPU
ADD_TO_PARENT MLIRPythonSources.Dialects.gpu
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectGPU.cpp
PRIVATE_LINK_LIBS
@@ -575,10 +579,11 @@ declare_mlir_python_extension(MLIRPythonExtension.Dialects.GPU.Nanobind
MLIRCAPIGPU
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.LLVM.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.LLVM.Pybind
MODULE_NAME _mlirDialectsLLVM
ADD_TO_PARENT MLIRPythonSources.Dialects.llvm
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectLLVM.cpp
PRIVATE_LINK_LIBS
@@ -588,10 +593,11 @@ declare_mlir_python_extension(MLIRPythonExtension.Dialects.LLVM.Nanobind
MLIRCAPILLVM
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.Quant.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.Quant.Pybind
MODULE_NAME _mlirDialectsQuant
ADD_TO_PARENT MLIRPythonSources.Dialects.quant
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectQuant.cpp
PRIVATE_LINK_LIBS
@@ -601,10 +607,11 @@ declare_mlir_python_extension(MLIRPythonExtension.Dialects.Quant.Nanobind
MLIRCAPIQuant
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.NVGPU.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.NVGPU.Pybind
MODULE_NAME _mlirDialectsNVGPU
ADD_TO_PARENT MLIRPythonSources.Dialects.nvgpu
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectNVGPU.cpp
PRIVATE_LINK_LIBS
@@ -614,10 +621,11 @@ declare_mlir_python_extension(MLIRPythonExtension.Dialects.NVGPU.Nanobind
MLIRCAPINVGPU
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.PDL.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.PDL.Pybind
MODULE_NAME _mlirDialectsPDL
ADD_TO_PARENT MLIRPythonSources.Dialects.pdl
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectPDL.cpp
PRIVATE_LINK_LIBS
@@ -627,10 +635,11 @@ declare_mlir_python_extension(MLIRPythonExtension.Dialects.PDL.Nanobind
MLIRCAPIPDL
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.SparseTensor.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.SparseTensor.Pybind
MODULE_NAME _mlirDialectsSparseTensor
ADD_TO_PARENT MLIRPythonSources.Dialects.sparse_tensor
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectSparseTensor.cpp
PRIVATE_LINK_LIBS
@@ -640,10 +649,11 @@ declare_mlir_python_extension(MLIRPythonExtension.Dialects.SparseTensor.Nanobind
MLIRCAPISparseTensor
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.Transform.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.Transform.Pybind
MODULE_NAME _mlirDialectsTransform
ADD_TO_PARENT MLIRPythonSources.Dialects.transform
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectTransform.cpp
PRIVATE_LINK_LIBS
@@ -653,10 +663,11 @@ declare_mlir_python_extension(MLIRPythonExtension.Dialects.Transform.Nanobind
MLIRCAPITransformDialect
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.IRDL.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.IRDL.Pybind
MODULE_NAME _mlirDialectsIRDL
ADD_TO_PARENT MLIRPythonSources.Dialects.irdl
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectIRDL.cpp
PRIVATE_LINK_LIBS
@@ -670,6 +681,7 @@ declare_mlir_python_extension(MLIRPythonExtension.AsyncDialectPasses
MODULE_NAME _mlirAsyncPasses
ADD_TO_PARENT MLIRPythonSources.Dialects.async
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
AsyncPasses.cpp
PRIVATE_LINK_LIBS
@@ -683,6 +695,7 @@ if(MLIR_ENABLE_EXECUTION_ENGINE)
MODULE_NAME _mlirExecutionEngine
ADD_TO_PARENT MLIRPythonSources.ExecutionEngine
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
ExecutionEngineModule.cpp
PRIVATE_LINK_LIBS
@@ -696,6 +709,7 @@ declare_mlir_python_extension(MLIRPythonExtension.GPUDialectPasses
MODULE_NAME _mlirGPUPasses
ADD_TO_PARENT MLIRPythonSources.Dialects.gpu
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
GPUPasses.cpp
PRIVATE_LINK_LIBS
@@ -708,6 +722,7 @@ declare_mlir_python_extension(MLIRPythonExtension.LinalgPasses
MODULE_NAME _mlirLinalgPasses
ADD_TO_PARENT MLIRPythonSources.Dialects.linalg
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
LinalgPasses.cpp
PRIVATE_LINK_LIBS
@@ -716,10 +731,11 @@ declare_mlir_python_extension(MLIRPythonExtension.LinalgPasses
MLIRCAPILinalg
)
declare_mlir_python_extension(MLIRPythonExtension.Dialects.SMT.Nanobind
declare_mlir_python_extension(MLIRPythonExtension.Dialects.SMT.Pybind
MODULE_NAME _mlirDialectsSMT
ADD_TO_PARENT MLIRPythonSources.Dialects.smt
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
DialectSMT.cpp
# Headers must be included explicitly so they are installed.
@@ -736,6 +752,7 @@ declare_mlir_python_extension(MLIRPythonExtension.SparseTensorDialectPasses
MODULE_NAME _mlirSparseTensorPasses
ADD_TO_PARENT MLIRPythonSources.Dialects.sparse_tensor
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
SparseTensorPasses.cpp
PRIVATE_LINK_LIBS
@@ -748,6 +765,7 @@ declare_mlir_python_extension(MLIRPythonExtension.TransformInterpreter
MODULE_NAME _mlirTransformInterpreter
ADD_TO_PARENT MLIRPythonSources.Dialects.transform
ROOT_DIR "${PYTHON_SOURCE_DIR}"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
TransformInterpreter.cpp
PRIVATE_LINK_LIBS
@@ -789,10 +807,23 @@ if(MLIR_INCLUDE_TESTS)
ADD_TO_PARENT MLIRPythonTestSources.Dialects.PythonTest
SOURCES "dialects/_python_test_ops_gen.py")
declare_mlir_python_extension(MLIRPythonTestSources.PythonTestExtensionPybind11
MODULE_NAME _mlirPythonTestPybind11
ADD_TO_PARENT MLIRPythonTestSources.Dialects
ROOT_DIR "${MLIR_SOURCE_DIR}/test/python/lib"
PYTHON_BINDINGS_LIBRARY pybind11
SOURCES
PythonTestModulePybind11.cpp
PRIVATE_LINK_LIBS
LLVMSupport
EMBED_CAPI_LINK_LIBS
MLIRCAPIPythonTestDialect
)
declare_mlir_python_extension(MLIRPythonTestSources.PythonTestExtensionNanobind
MODULE_NAME _mlirPythonTestNanobind
ADD_TO_PARENT MLIRPythonTestSources.Dialects
ROOT_DIR "${MLIR_SOURCE_DIR}/test/python/lib"
PYTHON_BINDINGS_LIBRARY nanobind
SOURCES
PythonTestModuleNanobind.cpp
PRIVATE_LINK_LIBS

View File

@@ -5,7 +5,12 @@
from ._python_test_ops_gen import *
def register_python_test_dialect(registry):
from .._mlir_libs import _mlirPythonTestNanobind
def register_python_test_dialect(registry, use_nanobind):
if use_nanobind:
from .._mlir_libs import _mlirPythonTestNanobind
_mlirPythonTestNanobind.register_dialect(registry)
_mlirPythonTestNanobind.register_dialect(registry)
else:
from .._mlir_libs import _mlirPythonTestPybind11
_mlirPythonTestPybind11.register_dialect(registry)

View File

@@ -1,4 +1,6 @@
nanobind>=2.9, <3.0
numpy>=1.19.5, <=2.1.2
pybind11>=2.10.0, <=2.13.6
PyYAML>=5.4.0, <=6.0.1
ml_dtypes>=0.1.0, <=0.6.0; python_version<"3.13" # provides several NumPy dtype extensions, including the bf16
ml_dtypes>=0.5.0, <=0.6.0; python_version>="3.13"

View File

@@ -1,4 +1,5 @@
# RUN: %PYTHON %s | FileCheck %s
# RUN: %PYTHON %s pybind11 | FileCheck %s
# RUN: %PYTHON %s nanobind | FileCheck %s
import sys
import typing
from typing import Union, Optional
@@ -9,14 +10,26 @@ import mlir.dialects.python_test as test
import mlir.dialects.tensor as tensor
import mlir.dialects.arith as arith
from mlir._mlir_libs._mlirPythonTestNanobind import (
TestAttr,
TestType,
TestTensorValue,
TestIntegerRankedTensorType,
)
if sys.argv[1] == "pybind11":
from mlir._mlir_libs._mlirPythonTestPybind11 import (
TestAttr,
TestType,
TestTensorValue,
TestIntegerRankedTensorType,
)
test.register_python_test_dialect(get_dialect_registry())
test.register_python_test_dialect(get_dialect_registry(), use_nanobind=False)
elif sys.argv[1] == "nanobind":
from mlir._mlir_libs._mlirPythonTestNanobind import (
TestAttr,
TestType,
TestTensorValue,
TestIntegerRankedTensorType,
)
test.register_python_test_dialect(get_dialect_registry(), use_nanobind=True)
else:
raise ValueError("Expected pybind11 or nanobind as argument")
def run(f):

View File

@@ -1,6 +1,7 @@
set(LLVM_OPTIONAL_SOURCES
PythonTestCAPI.cpp
PythonTestDialect.cpp
PythonTestModulePybind11.cpp
PythonTestModuleNanobind.cpp
)

View File

@@ -0,0 +1,118 @@
//===- PythonTestModule.cpp - Python extension for the PythonTest dialect -===//
//
// 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 pybind11 edition of the PythonTest dialect module.
//===----------------------------------------------------------------------===//
#include "PythonTestCAPI.h"
#include "mlir-c/BuiltinAttributes.h"
#include "mlir-c/BuiltinTypes.h"
#include "mlir-c/IR.h"
#include "mlir/Bindings/Python/PybindAdaptors.h"
namespace py = pybind11;
using namespace mlir::python::adaptors;
using namespace pybind11::literals;
static bool mlirTypeIsARankedIntegerTensor(MlirType t) {
return mlirTypeIsARankedTensor(t) &&
mlirTypeIsAInteger(mlirShapedTypeGetElementType(t));
}
PYBIND11_MODULE(_mlirPythonTestPybind11, m) {
m.def(
"register_python_test_dialect",
[](MlirContext context, bool load) {
MlirDialectHandle pythonTestDialect =
mlirGetDialectHandle__python_test__();
mlirDialectHandleRegisterDialect(pythonTestDialect, context);
if (load) {
mlirDialectHandleLoadDialect(pythonTestDialect, context);
}
},
py::arg("context"), py::arg("load") = true);
m.def(
"register_dialect",
[](MlirDialectRegistry registry) {
MlirDialectHandle pythonTestDialect =
mlirGetDialectHandle__python_test__();
mlirDialectHandleInsertDialect(pythonTestDialect, registry);
},
py::arg("registry"));
mlir_attribute_subclass(m, "TestAttr",
mlirAttributeIsAPythonTestTestAttribute,
mlirPythonTestTestAttributeGetTypeID)
.def_classmethod(
"get",
[](const py::object &cls, MlirContext ctx) {
return cls(mlirPythonTestTestAttributeGet(ctx));
},
py::arg("cls"), py::arg("context") = py::none());
mlir_type_subclass(m, "TestType", mlirTypeIsAPythonTestTestType,
mlirPythonTestTestTypeGetTypeID)
.def_classmethod(
"get",
[](const py::object &cls, MlirContext ctx) {
return cls(mlirPythonTestTestTypeGet(ctx));
},
py::arg("cls"), py::arg("context") = py::none());
auto typeCls =
mlir_type_subclass(m, "TestIntegerRankedTensorType",
mlirTypeIsARankedIntegerTensor,
py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr("RankedTensorType"))
.def_classmethod(
"get",
[](const py::object &cls, std::vector<int64_t> shape,
unsigned width, MlirContext ctx) {
MlirAttribute encoding = mlirAttributeGetNull();
return cls(mlirRankedTensorTypeGet(
shape.size(), shape.data(), mlirIntegerTypeGet(ctx, width),
encoding));
},
"cls"_a, "shape"_a, "width"_a, "context"_a = py::none());
assert(py::hasattr(typeCls.get_class(), "static_typeid") &&
"TestIntegerRankedTensorType has no static_typeid");
MlirTypeID mlirRankedTensorTypeID = mlirRankedTensorTypeGetTypeID();
py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr(MLIR_PYTHON_CAPI_TYPE_CASTER_REGISTER_ATTR)(mlirRankedTensorTypeID,
"replace"_a = true)(
pybind11::cpp_function([typeCls](const py::object &mlirType) {
return typeCls.get_class()(mlirType);
}));
auto valueCls = mlir_value_subclass(m, "TestTensorValue",
mlirTypeIsAPythonTestTestTensorValue)
.def("is_null", [](MlirValue &self) {
return mlirValueIsNull(self);
});
py::module::import(MAKE_MLIR_PYTHON_QUALNAME("ir"))
.attr(MLIR_PYTHON_CAPI_VALUE_CASTER_REGISTER_ATTR)(
mlirRankedTensorTypeID)(
pybind11::cpp_function([valueCls](const py::object &valueObj) {
py::object capsule = mlirApiObjectToCapsule(valueObj);
MlirValue v = mlirPythonCapsuleToValue(capsule.ptr());
MlirType t = mlirValueGetType(v);
// This is hyper-specific in order to exercise/test registering a
// value caster from cpp (but only for a single test case; see
// testTensorValue python_test.py).
if (mlirShapedTypeHasStaticShape(t) &&
mlirShapedTypeGetDimSize(t, 0) == 1 &&
mlirShapedTypeGetDimSize(t, 1) == 2 &&
mlirShapedTypeGetDimSize(t, 2) == 3)
return valueCls.get_class()(valueObj);
return valueObj;
}));
}

View File

@@ -26,7 +26,7 @@ export PYTHONPATH="$python_package_dir"
OUTPUT="$(
echo "### AUTOGENERATED from core_named_ops.py" && \
echo "### To regenerate, run: bin/update_core_linalg_named_ops.sh" && \
"$python_exe" -m mlir.dialects.linalg.opdsl.dump_oplib.ops.core_named_ops \
"$python_exe" -m mlir.dialects.linalg.opdsl.dump_oplib .ops.core_named_ops \
)"
echo "$OUTPUT" > "$dest_file"
echo "Success."

View File

@@ -1048,6 +1048,32 @@ filegroup(
]),
)
cc_library(
name = "MLIRBindingsPythonHeaders",
includes = [
"include",
],
textual_hdrs = [":MLIRBindingsPythonHeaderFiles"],
deps = [
":CAPIIRHeaders",
"@pybind11",
"@rules_python//python/cc:current_py_cc_headers",
],
)
cc_library(
name = "MLIRBindingsPythonHeadersAndDeps",
includes = [
"include",
],
textual_hdrs = [":MLIRBindingsPythonHeaderFiles"],
deps = [
":CAPIIR",
"@pybind11",
"@rules_python//python/cc:current_py_cc_headers",
],
)
cc_library(
name = "MLIRBindingsPythonNanobindHeaders",
includes = [
@@ -1061,11 +1087,6 @@ cc_library(
],
)
alias(
name = "MLIRBindingsPythonHeaders",
actual = ":MLIRBindingsPythonNanobindHeaders",
)
cc_library(
name = "MLIRBindingsPythonNanobindHeadersAndDeps",
includes = [
@@ -1079,11 +1100,6 @@ cc_library(
],
)
alias(
name = "MLIRBindingsPythonHeadersAndDeps",
actual = ":MLIRBindingsPythonNanobindHeadersAndDeps",
)
# These flags are needed for pybind11 to work.
PYBIND11_COPTS = [
"-fexceptions",
@@ -1131,7 +1147,7 @@ cc_library(
":CAPIIR",
":CAPIInterfaces",
":CAPITransforms",
":MLIRBindingsPythonHeadersAndDeps",
":MLIRBindingsPythonNanobindHeadersAndDeps",
":Support",
":config",
"//llvm:Support",
@@ -1154,7 +1170,7 @@ cc_library(
":CAPIDebugHeaders",
":CAPIIRHeaders",
":CAPITransformsHeaders",
":MLIRBindingsPythonHeaders",
":MLIRBindingsPythonNanobindHeaders",
":Support",
":config",
"//llvm:Support",
@@ -1204,7 +1220,7 @@ cc_binary(
linkstatic = 0,
deps = [
":CAPIIR",
":MLIRBindingsPythonHeadersAndDeps",
":MLIRBindingsPythonNanobindHeadersAndDeps",
"@nanobind",
],
)
@@ -1222,7 +1238,7 @@ cc_binary(
deps = [
":CAPIIR",
":CAPILinalg",
":MLIRBindingsPythonHeadersAndDeps",
":MLIRBindingsPythonNanobindHeadersAndDeps",
"@nanobind",
],
)
@@ -1237,7 +1253,7 @@ cc_binary(
deps = [
":CAPIIR",
":CAPILLVM",
":MLIRBindingsPythonHeadersAndDeps",
":MLIRBindingsPythonNanobindHeadersAndDeps",
"@nanobind",
],
)
@@ -1252,7 +1268,7 @@ cc_binary(
deps = [
":CAPIIR",
":CAPIQuant",
":MLIRBindingsPythonHeadersAndDeps",
":MLIRBindingsPythonNanobindHeadersAndDeps",
"@nanobind",
],
)
@@ -1267,7 +1283,7 @@ cc_binary(
deps = [
":CAPIIR",
":CAPISparseTensor",
":MLIRBindingsPythonHeadersAndDeps",
":MLIRBindingsPythonNanobindHeadersAndDeps",
"@nanobind",
],
)
@@ -1282,7 +1298,7 @@ cc_binary(
linkstatic = 0,
deps = [
":CAPIExecutionEngine",
":MLIRBindingsPythonHeadersAndDeps",
":MLIRBindingsPythonNanobindHeadersAndDeps",
"@nanobind",
"@rules_python//python/cc:current_py_cc_headers",
],
@@ -1298,7 +1314,7 @@ cc_binary(
linkstatic = 0,
deps = [
":CAPILinalg",
":MLIRBindingsPythonHeadersAndDeps",
":MLIRBindingsPythonNanobindHeadersAndDeps",
"@nanobind",
"@rules_python//python/cc:current_py_cc_headers",
],