[Mlir-commits] [mlir] 99b1c42 - [mlir][Python] Add Windows DLL loader to get python extensions working there.

Stella Laurenzo llvmlistbot at llvm.org
Wed Nov 11 09:59:39 PST 2020


Author: Stella Laurenzo
Date: 2020-11-11T09:54:47-08:00
New Revision: 99b1c42fd3ac405471ff56402c270a1056383f82

URL: https://github.com/llvm/llvm-project/commit/99b1c42fd3ac405471ff56402c270a1056383f82
DIFF: https://github.com/llvm/llvm-project/commit/99b1c42fd3ac405471ff56402c270a1056383f82.diff

LOG: [mlir][Python] Add Windows DLL loader to get python extensions working there.

Differential Revision: https://reviews.llvm.org/D90958

Added: 
    mlir/lib/Bindings/Python/mlir/_dlloader.py

Modified: 
    mlir/cmake/modules/AddMLIRPythonExtension.cmake
    mlir/lib/Bindings/Python/CMakeLists.txt
    mlir/lib/Bindings/Python/mlir/__init__.py

Removed: 
    


################################################################################
diff  --git a/mlir/cmake/modules/AddMLIRPythonExtension.cmake b/mlir/cmake/modules/AddMLIRPythonExtension.cmake
index 8a74675e2543..43ad869a400b 100644
--- a/mlir/cmake/modules/AddMLIRPythonExtension.cmake
+++ b/mlir/cmake/modules/AddMLIRPythonExtension.cmake
@@ -75,6 +75,15 @@ function(add_mlir_python_extension libname extname)
     SUFFIX "${PYTHON_MODULE_SUFFIX}${PYTHON_MODULE_EXTENSION}"
   )
 
+  if(WIN32)
+    # Need to also set the RUNTIME_OUTPUT_DIRECTORY on Windows in order to
+    # control where the .dll gets written.
+    set_target_properties(
+      ${libname} PROPERTIES
+      RUNTIME_OUTPUT_DIRECTORY ${LLVM_BINARY_DIR}/python
+    )
+  endif()
+
   # pybind11 requires binding code to be compiled with -fvisibility=hidden
   # For static linkage, better code can be generated if the entire project
   # compiles that way, but that is not enforced here. Instead, include a linker

diff  --git a/mlir/lib/Bindings/Python/CMakeLists.txt b/mlir/lib/Bindings/Python/CMakeLists.txt
index bf6f1d8d5567..596bff23093e 100644
--- a/mlir/lib/Bindings/Python/CMakeLists.txt
+++ b/mlir/lib/Bindings/Python/CMakeLists.txt
@@ -6,6 +6,8 @@ add_custom_target(MLIRBindingsPythonExtension)
 
 set(PY_SRC_FILES
   mlir/__init__.py
+  mlir/_dlloader.py
+  mlir/ir.py
   mlir/dialects/__init__.py
   mlir/ir.py
   mlir/passmanager.py

diff  --git a/mlir/lib/Bindings/Python/mlir/__init__.py b/mlir/lib/Bindings/Python/mlir/__init__.py
index c63c4332be68..8e027f64bdb6 100644
--- a/mlir/lib/Bindings/Python/mlir/__init__.py
+++ b/mlir/lib/Bindings/Python/mlir/__init__.py
@@ -13,6 +13,11 @@
   "passmanager",
 ]
 
+# The _dlloader takes care of platform specific setup before we try to
+# load a shared library.
+from . import _dlloader
+_dlloader.preload_dependency("MLIRPublicAPI")
+
 # Expose the corresponding C-Extension module with a well-known name at this
 # top-level module. This allows relative imports like the following to
 # function:

diff  --git a/mlir/lib/Bindings/Python/mlir/_dlloader.py b/mlir/lib/Bindings/Python/mlir/_dlloader.py
new file mode 100644
index 000000000000..454a7b7f137f
--- /dev/null
+++ b/mlir/lib/Bindings/Python/mlir/_dlloader.py
@@ -0,0 +1,59 @@
+#  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
+
+import os
+import platform
+
+_is_windows = platform.system() == "Windows"
+_this_directory = os.path.dirname(__file__)
+
+# The standard LLVM build/install tree for Windows is laid out as:
+#   bin/
+#     MLIRPublicAPI.dll
+#   python/
+#     _mlir.*.pyd (dll extension)
+#     mlir/
+#       _dlloader.py (this file)
+# First check the python/ directory level for DLLs co-located with the pyd
+# file, and then fall back to searching the bin/ directory.
+# TODO: This should be configurable at some point.
+_dll_search_path = [
+  os.path.join(_this_directory, ".."),
+  os.path.join(_this_directory, "..", "..", "bin"),
+]
+
+# Stash loaded DLLs to keep them alive.
+_loaded_dlls = []
+
+def preload_dependency(public_name):
+  """Preloads a dylib by its soname or DLL name.
+
+  On Windows and Linux, doing this prior to loading a dependency will populate
+  the library in the flat namespace so that a subsequent library that depend
+  on it will resolve to this preloaded version.
+
+  On OSX, resolution is completely path based so this facility no-ops. On
+  Linux, as long as RPATHs are setup properly, resolution is path based but
+  this facility can still act as an escape hatch for relocatable distributions.
+  """
+  if _is_windows:
+    _preload_dependency_windows(public_name)
+
+
+def _preload_dependency_windows(public_name):
+  dll_basename = public_name + ".dll"
+  found_path = None
+  for search_dir in _dll_search_path:
+    candidate_path = os.path.join(search_dir, dll_basename)
+    if os.path.exists(candidate_path):
+      found_path = candidate_path
+      break
+
+  if found_path is None:
+    raise RuntimeError(
+      f"Unable to find dependency DLL {dll_basename} in search "
+      f"path {_dll_search_path}")
+
+  import ctypes
+  _loaded_dlls.append(ctypes.CDLL(found_path))


        


More information about the Mlir-commits mailing list