Files
clang-p2996/mlir/cmake/modules/AddMLIRPythonExtension.cmake
Alex Zinenko c5a6712f8c [mlir] Add basic support for attributes in ODS-generated Python bindings
In ODS, attributes of an operation can be provided as a part of the "arguments"
field, together with operands. Such attributes are accepted by the op builder
and have accessors generated.

Implement similar functionality for ODS-generated op-specific Python bindings:
the `__init__` method now accepts arguments together with operands, in the same
order as in the ODS `arguments` field; the instance properties are introduced
to OpView classes to access the attributes.

This initial implementation accepts and returns instances of the corresponding
attribute class, and not the underlying values since the mapping scheme of the
value types between C++, C and Python is not yet clear. Default-valued
attributes are not supported as that would require Python to be able to parse
C++ literals.

Since attributes in ODS are tightely related to the actual C++ type system,
provide a separate Tablegen file with the mapping between ODS storage type for
attributes (typically, the underlying C++ attribute class), and the
corresponding class name. So far, this might look unnecessary since all names
match exactly, but this is not necessarily the cases for non-standard,
out-of-tree attributes, which may also be placed in non-default namespaces or
Python modules. This also allows out-of-tree users to generate Python bindings
without having to modify the bindings generator itself. Storage type was
preferred over the Tablegen "def" of the attribute class because ODS
essentially encodes attribute _constraints_ rather than classes, e.g. there may
be many Tablegen "def"s in the ODS that correspond to the same attribute type
with additional constraints

The presence of the explicit mapping requires the change in the .td file
structure: instead of just calling the bindings generator directly on the main
ODS file of the dialect, it becomes necessary to create a new file that
includes the main ODS file of the dialect and provides the mapping for
attribute types. Arguably, this approach offers better separability of the
Python bindings in the build system as the main dialect no longer needs to know
that it is being processed by the bindings generator.

Reviewed By: stellaraccident

Differential Revision: https://reviews.llvm.org/D91542
2020-11-17 11:47:37 +01:00

149 lines
5.5 KiB
CMake

################################################################################
# Build python extension
################################################################################
function(add_mlir_python_extension libname extname)
cmake_parse_arguments(ARG
""
"INSTALL_DIR"
"SOURCES;LINK_LIBS"
${ARGN})
if (ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR " Unhandled arguments to add_mlir_python_extension(${libname}, ... : ${ARG_UNPARSED_ARGUMENTS}")
endif()
if ("${ARG_SOURCES}" STREQUAL "")
message(FATAL_ERROR " Missing SOURCES argument to add_mlir_python_extension(${libname}, ...")
endif()
# Normally on unix-like platforms, extensions are built as "MODULE" libraries
# and do not explicitly link to the python shared object. This allows for
# some greater deployment flexibility since the extension will bind to
# symbols in the python interpreter on load. However, it also keeps the
# linker from erroring on undefined symbols, leaving this to (usually obtuse)
# runtime errors. Building in "SHARED" mode with an explicit link to the
# python libraries allows us to build with the expectation of no undefined
# symbols, which is better for development. Note that not all python
# configurations provide build-time libraries to link against, in which
# case, we fall back to MODULE linking.
if(Python3_LIBRARIES STREQUAL "" OR NOT MLIR_PYTHON_BINDINGS_VERSION_LOCKED)
set(PYEXT_LINK_MODE MODULE)
set(PYEXT_LIBADD)
else()
set(PYEXT_LINK_MODE SHARED)
set(PYEXT_LIBADD ${Python3_LIBRARIES})
endif()
# 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).
add_library(${libname} ${PYEXT_LINK_MODE}
${ARG_SOURCES}
)
target_include_directories(${libname} PRIVATE
"${Python3_INCLUDE_DIRS}"
"${pybind11_INCLUDE_DIR}"
)
# The extension itself must be compiled with RTTI and exceptions enabled.
# Also, some warning classes triggered by pybind11 are disabled.
target_compile_options(${libname} PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
# Enable RTTI and exceptions.
-frtti -fexceptions
# Noisy pybind warnings
-Wno-unused-value
-Wno-covered-switch-default
>
$<$<CXX_COMPILER_ID:MSVC>:
# Enable RTTI and exceptions.
/EHsc /GR>
)
# Configure the output to match python expectations.
set_target_properties(
${libname} PROPERTIES
# Build-time RPath layouts require to be a directory one up from the
# binary root.
# TODO: Don't reference the LLVM_BINARY_DIR here: the invariant is that
# the output directory must be at the same level of the lib directory
# where libMLIR.so is installed. This is presently not optimal from a
# project separation perspective and a discussion on how to better
# segment MLIR libraries needs to happen.
LIBRARY_OUTPUT_DIRECTORY ${LLVM_BINARY_DIR}/python
OUTPUT_NAME "${extname}"
PREFIX "${PYTHON_MODULE_PREFIX}"
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
# script that explicitly hides anything but the PyInit_* symbols, allowing gc
# to take place.
set_target_properties(${libname} PROPERTIES CXX_VISIBILITY_PRESET "hidden")
# Python extensions depends *only* on the public API and LLVMSupport unless
# if further dependencies are added explicitly.
target_link_libraries(${libname}
PRIVATE
MLIRPublicAPI
LLVMSupport
${ARG_LINK_LIBS}
${PYEXT_LIBADD}
)
target_link_options(${libname}
PRIVATE
# On Linux, disable re-export of any static linked libraries that
# came through.
$<$<PLATFORM_ID:Linux>:LINKER:--exclude-libs,ALL>
)
llvm_setup_rpath(${libname})
################################################################################
# Install
################################################################################
if (ARG_INSTALL_DIR)
install(TARGETS ${libname}
COMPONENT ${libname}
LIBRARY DESTINATION ${ARG_INSTALL_DIR}
ARCHIVE DESTINATION ${ARG_INSTALL_DIR}
# NOTE: Even on DLL-platforms, extensions go in the lib directory tree.
RUNTIME DESTINATION ${ARG_INSTALL_DIR}
)
endif()
if (NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(
install-${libname}
DEPENDS ${libname}
COMPONENT ${libname})
endif()
endfunction()
function(add_mlir_dialect_python_bindings tblgen_target filename dialectname)
set(LLVM_TARGET_DEFINITIONS ${filename})
mlir_tablegen("${dialectname}.py" -gen-python-op-bindings
-bind-dialect=${dialectname})
add_public_tablegen_target(${tblgen_target})
add_custom_command(
TARGET ${tblgen_target} POST_BUILD
COMMENT "Copying generated python source \"dialects/${dialectname}.py\""
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_BINARY_DIR}/${dialectname}.py"
"${PROJECT_BINARY_DIR}/python/mlir/dialects/${dialectname}.py")
endfunction()