Skip to content

Commit 722475a

Browse files
Initial boiler-plate for python bindings.
Summary: * Native '_mlir' extension module. * Python mlir/__init__.py trampoline module. * Lit test that checks a message. * Uses some cmake configurations that have worked for me in the past but likely needs further elaboration. Subscribers: mgorny, mehdi_amini, rriddle, jpienaar, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, stephenneuendorffer, Joonsoo, grosul1, Kayjukh, jurahul, msifontes Tags: #mlir Differential Revision: https://reviews.llvm.org/D83279
1 parent 56ae2ce commit 722475a

File tree

12 files changed

+180
-4
lines changed

12 files changed

+180
-4
lines changed

mlir/CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,38 @@ option(MLIR_INCLUDE_TESTS
5151
option(MLIR_INCLUDE_INTEGRATION_TESTS
5252
"Generate build targets for the MLIR integration tests.")
5353

54+
#-------------------------------------------------------------------------------
55+
# Python Bindings Configuration
56+
# Requires:
57+
# The pybind11 library can be found (set with -DPYBIND_DIR=...)
58+
# The python executable is correct (set with -DPYTHON_EXECUTABLE=...)
59+
#
60+
# Version locking
61+
# ---------------
62+
# By default, python extensions are version locked to specific Python libraries.
63+
# This linking mode is somewhat more consistent across platforms and surfaces
64+
# undefined symbols at link time (vs runtime). It is suitable for development
65+
# workflows but can be disabled for more flexible deployment by
66+
# setting -DMLIR_PYTHON_BINDINGS_VERSION_LOCKED=OFF
67+
#-------------------------------------------------------------------------------
68+
69+
set(MLIR_BINDINGS_PYTHON_ENABLED 0 CACHE BOOL
70+
"Enables building of Python bindings.")
71+
set(MLIR_PYTHON_BINDINGS_VERSION_LOCKED 1 CACHE BOOL
72+
"Links to specific python libraries, resolving all symbols.")
73+
74+
if(MLIR_BINDINGS_PYTHON_ENABLED)
75+
find_package(PythonInterp REQUIRED)
76+
find_package(PythonLibs REQUIRED)
77+
message(STATUS "Found python include dirs: ${PYTHON_INCLUDE_DIRS}")
78+
message(STATUS "Found ppython libraries: ${PYTHON_LIBRARIES}")
79+
find_package(pybind11 CONFIG REQUIRED)
80+
message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}")
81+
message(STATUS "Python prefix = '${PYTHON_MODULE_PREFIX}', "
82+
"suffix = '${PYTHON_MODULE_SUFFIX}', "
83+
"extension = '${PYTHON_MODULE_EXTENSION}")
84+
endif()
85+
5486
include_directories( "include")
5587
include_directories( ${MLIR_INCLUDE_DIR})
5688

mlir/lib/Bindings/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
if(MLIR_BINDINGS_PYTHON_ENABLED)
2+
add_subdirectory(Python)
3+
endif()
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Normally on unix-like platforms, extensions are built as "MODULE" libraries
2+
# and do not explicitly link to the python shared object. This allows for
3+
# some greater deployment flexibility since the extension will bind to
4+
# symbols in the python interpreter on load. However, it also keeps the
5+
# linker from erroring on undefined symbols, leaving this to (usually obtuse)
6+
# runtime errors. Building in "SHARED" mode with an explicit link to the
7+
# python libraries allows us to build with the expectation of no undefined
8+
# symbols, which is better for development.
9+
if(MLIR_PYTHON_BINDINGS_VERSION_LOCKED)
10+
set(PYEXT_LINK_MODE SHARED)
11+
set(PYEXT_LIBADD ${PYTHON_LIBRARIES})
12+
else()
13+
set(PYEXT_LINK_MODE MODULE)
14+
set(PYEXT_LIBADD)
15+
endif()
16+
17+
# The actual extension library produces a shared-object or DLL and has
18+
# sources that must be compiled in accordance with pybind11 needs (RTTI and
19+
# exceptions).
20+
add_library(MLIRBindingsPythonExtension ${PYEXT_LINK_MODE}
21+
MainModule.cpp
22+
)
23+
24+
target_include_directories(MLIRBindingsPythonExtension PRIVATE
25+
"${PYTHON_INCLUDE_DIRS}"
26+
"${pybind11_INCLUDE_DIRS}")
27+
28+
# The extension itself must be compiled with RTTI and exceptions enabled.
29+
# Also, some warning classes triggered by pybind11 are disabled.
30+
target_compile_options(MLIRBindingsPythonExtension PRIVATE
31+
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
32+
# Enable RTTI and exceptions.
33+
-frtti -fexceptions
34+
# Noisy pybind warnings
35+
-Wno-unused-value
36+
-Wno-covered-switch-default
37+
>
38+
$<$<CXX_COMPILER_ID:MSVC>:
39+
# Enable RTTI and exceptions.
40+
/EHsc /GR>
41+
)
42+
43+
# Configure the output to match python expectations.
44+
set_target_properties(
45+
MLIRBindingsPythonExtension PROPERTIES
46+
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
47+
OUTPUT_NAME "_mlir"
48+
PREFIX "${PYTHON_MODULE_PREFIX}"
49+
SUFFIX "${PYTHON_MODULE_SUFFIX}${PYTHON_MODULE_EXTENSION}"
50+
)
51+
52+
# pybind11 requires binding code to be compiled with -fvisibility=hidden
53+
# For static linkage, better code can be generated if the entire project
54+
# compiles that way, but that is not enforced here. Instead, include a linker
55+
# script that explicitly hides anything but the PyInit_* symbols, allowing gc
56+
# to take place.
57+
# TODO: Add a Windows .def file and figure out the right thing to do on MacOS.
58+
set_target_properties(
59+
MLIRBindingsPythonExtension PROPERTIES CXX_VISIBILITY_PRESET "hidden")
60+
if(NOT MSVC AND NOT APPLE)
61+
set_target_properties(MLIRBindingsPythonExtension
62+
PROPERTIES LINK_FLAGS
63+
"-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/unix_version.lds")
64+
endif()
65+
66+
target_link_libraries(MLIRBindingsPythonExtension
67+
PRIVATE
68+
MLIRIR
69+
${PYEXT_LIBADD}
70+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===- MainModule.cpp - Main 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+
#include <tuple>
10+
11+
#include <pybind11/pybind11.h>
12+
13+
#include "mlir/IR/MLIRContext.h"
14+
15+
using namespace mlir;
16+
17+
PYBIND11_MODULE(_mlir, m) {
18+
m.doc() = "MLIR Python Native Extension";
19+
20+
m.def("get_test_value", []() {
21+
// This is just calling a method on the MLIRContext as a smoketest
22+
// for linkage.
23+
MLIRContext context;
24+
return std::make_tuple(std::string("From the native module"),
25+
context.isMultithreadingEnabled());
26+
});
27+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2+
# See https://llvm.org/LICENSE.txt for license information.
3+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
# Note that the only function of this module is currently to load the
6+
# native module and re-export its symbols. In the future, this file is
7+
# reserved as a trampoline to handle environment specific loading needs
8+
# and arbitrate any one-time initialization needed in various shared-library
9+
# scenarios.
10+
11+
from _mlir import *
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
global: PyInit__mlir;
3+
local: *;
4+
};

mlir/lib/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
add_flag_if_supported("-Werror=global-constructors" WERROR_GLOBAL_CONSTRUCTOR)
33

44
add_subdirectory(Analysis)
5+
add_subdirectory(Bindings)
56
add_subdirectory(Conversion)
67
add_subdirectory(Dialect)
78
add_subdirectory(EDSC)
@@ -15,4 +16,4 @@ add_subdirectory(Support)
1516
add_subdirectory(TableGen)
1617
add_subdirectory(Target)
1718
add_subdirectory(Transforms)
18-
add_subdirectory(Translation)
19+
add_subdirectory(Translation)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
if not config.enable_bindings_python:
2+
config.unsupported = True
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# RUN: %PYTHON %s | FileCheck %s
2+
3+
import mlir
4+
5+
# CHECK: From the native module
6+
print(mlir.get_test_value())

mlir/test/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_subdirectory(SDBM)
44
add_subdirectory(lib)
55

66
llvm_canonicalize_cmake_booleans(
7+
MLIR_BINDINGS_PYTHON_ENABLED
78
LLVM_BUILD_EXAMPLES
89
MLIR_CUDA_CONVERSIONS_ENABLED
910
MLIR_CUDA_RUNNER_ENABLED
@@ -83,6 +84,12 @@ if(MLIR_VULKAN_RUNNER_ENABLED)
8384
)
8485
endif()
8586

87+
if(MLIR_BINDINGS_PYTHON_ENABLED)
88+
list(APPEND MLIR_TEST_DEPENDS
89+
MLIRBindingsPythonExtension
90+
)
91+
endif()
92+
8693
add_lit_testsuite(check-mlir "Running the MLIR regression tests"
8794
${CMAKE_CURRENT_BINARY_DIR}
8895
DEPENDS ${MLIR_TEST_DEPENDS}

mlir/test/lit.cfg.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)
2222

2323
# suffixes: A list of file extensions to treat as test files.
24-
config.suffixes = ['.td', '.mlir', '.toy', '.ll', '.tc']
24+
config.suffixes = ['.td', '.mlir', '.toy', '.ll', '.tc', '.py']
2525

2626
# test_source_root: The root path where tests are located.
2727
config.test_source_root = os.path.dirname(__file__)
@@ -41,7 +41,8 @@
4141
# excludes: A list of directories to exclude from the testsuite. The 'Inputs'
4242
# subdirectories contain auxiliary inputs for various tests in their parent
4343
# directories.
44-
config.excludes = ['Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt']
44+
config.excludes = ['Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt',
45+
'lit.cfg.py', 'lit.site.cfg.py']
4546

4647
# test_source_root: The root path where tests are located.
4748
config.test_source_root = os.path.dirname(__file__)
@@ -62,6 +63,7 @@
6263

6364
# The following tools are optional
6465
tools.extend([
66+
ToolSubst('%PYTHON', config.python_executable),
6567
ToolSubst('toy-ch1', unresolved='ignore'),
6668
ToolSubst('toy-ch2', unresolved='ignore'),
6769
ToolSubst('toy-ch3', unresolved='ignore'),
@@ -71,7 +73,7 @@
7173
ToolSubst('%linalg_test_lib_dir', config.linalg_test_lib_dir, unresolved='ignore'),
7274
ToolSubst('%mlir_runner_utils_dir', config.mlir_runner_utils_dir, unresolved='ignore'),
7375
ToolSubst('%rocm_wrapper_library_dir', config.rocm_wrapper_library_dir, unresolved='ignore'),
74-
ToolSubst('%vulkan_wrapper_library_dir', config.vulkan_wrapper_library_dir, unresolved='ignore')
76+
ToolSubst('%vulkan_wrapper_library_dir', config.vulkan_wrapper_library_dir, unresolved='ignore'),
7577
])
7678

7779
llvm_config.add_tool_substitutions(tools, tool_dirs)
@@ -89,3 +91,13 @@
8991
# to be available for JIT tests.
9092
if config.target_triple:
9193
config.available_features.add('default_triple')
94+
95+
# Add the python path for both the source and binary tree.
96+
# Note that presently, the python sources come from the source tree and the
97+
# binaries come from the build tree. This should be unified to the build tree
98+
# by copying/linking sources to build.
99+
if config.enable_bindings_python:
100+
llvm_config.with_environment('PYTHONPATH', [
101+
os.path.join(config.mlir_src_root, "lib", "Bindings", "Python"),
102+
os.path.join(config.mlir_obj_root, "lib", "Bindings", "Python"),
103+
], append_path=True)

mlir/test/lit.site.cfg.py.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ config.rocm_wrapper_library_dir = "@MLIR_ROCM_WRAPPER_LIBRARY_DIR@"
4343
config.enable_rocm_runner = @MLIR_ROCM_RUNNER_ENABLED@
4444
config.vulkan_wrapper_library_dir = "@MLIR_VULKAN_WRAPPER_LIBRARY_DIR@"
4545
config.enable_vulkan_runner = @MLIR_VULKAN_RUNNER_ENABLED@
46+
config.enable_bindings_python = @MLIR_BINDINGS_PYTHON_ENABLED@
4647

4748
# Support substitution of the tools_dir with user parameters. This is
4849
# used when we can't determine the tool dir at configuration time.

0 commit comments

Comments
 (0)