1 //===-- mlir-c/Interop.h - Constants for Python/C-API interop -----*- C -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM
4 // Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This header declares constants and helpers necessary for C-level
11 // interop with the MLIR Python extension module. Since the Python bindings
12 // are a thin wrapper around the MLIR C-API, a further C-API is not provided
13 // specifically for the Python extension. Instead, simple facilities are
14 // provided for translating between Python types and corresponding MLIR C-API
15 // types.
16 //
17 // This header is standalone, requiring nothing beyond normal linking against
18 // the Python implementation.
19 //===----------------------------------------------------------------------===//
20 
21 #ifndef MLIR_C_BINDINGS_PYTHON_INTEROP_H
22 #define MLIR_C_BINDINGS_PYTHON_INTEROP_H
23 
24 // We *should*, in theory, include Python.h here in order to import the correct
25 // definitions for what we need below, however, importing Python.h directly on
26 // Windows results in the enforcement of either pythonX.lib or pythonX_d.lib
27 // depending on the build flavor. Instead, we rely on the fact that this file
28 // (Interop.h) is always included AFTER pybind11 and will therefore have access
29 // to the definitions from Python.h in addition to having a workaround applied
30 // through the pybind11 headers that allows us to control which python library
31 // is used.
32 #if !defined(_MSC_VER)
33 #include <Python.h>
34 #endif
35 
36 #include "mlir-c/AffineExpr.h"
37 #include "mlir-c/AffineMap.h"
38 #include "mlir-c/ExecutionEngine.h"
39 #include "mlir-c/IR.h"
40 #include "mlir-c/IntegerSet.h"
41 #include "mlir-c/Pass.h"
42 
43 // The 'mlir' Python package is relocatable and supports co-existing in multiple
44 // projects. Each project must define its outer package prefix with this define
45 // in order to provide proper isolation and local name resolution.
46 // The default is for the upstream "import mlir" package layout.
47 // Note that this prefix is internally stringified, allowing it to be passed
48 // unquoted on the compiler command line without shell quote escaping issues.
49 #ifndef MLIR_PYTHON_PACKAGE_PREFIX
50 #define MLIR_PYTHON_PACKAGE_PREFIX mlir.
51 #endif
52 
53 // Makes a fully-qualified name relative to the MLIR python package.
54 #define MLIR_PYTHON_STRINGIZE(s) #s
55 #define MLIR_PYTHON_STRINGIZE_ARG(arg) MLIR_PYTHON_STRINGIZE(arg)
56 #define MAKE_MLIR_PYTHON_QUALNAME(local)                                       \
57   MLIR_PYTHON_STRINGIZE_ARG(MLIR_PYTHON_PACKAGE_PREFIX) local
58 
59 #define MLIR_PYTHON_CAPSULE_AFFINE_EXPR                                        \
60   MAKE_MLIR_PYTHON_QUALNAME("ir.AffineExpr._CAPIPtr")
61 #define MLIR_PYTHON_CAPSULE_AFFINE_MAP                                         \
62   MAKE_MLIR_PYTHON_QUALNAME("ir.AffineMap._CAPIPtr")
63 #define MLIR_PYTHON_CAPSULE_ATTRIBUTE                                          \
64   MAKE_MLIR_PYTHON_QUALNAME("ir.Attribute._CAPIPtr")
65 #define MLIR_PYTHON_CAPSULE_CONTEXT                                            \
66   MAKE_MLIR_PYTHON_QUALNAME("ir.Context._CAPIPtr")
67 #define MLIR_PYTHON_CAPSULE_DIALECT_REGISTRY                                   \
68   MAKE_MLIR_PYTHON_QUALNAME("ir.DialectRegistry._CAPIPtr")
69 #define MLIR_PYTHON_CAPSULE_EXECUTION_ENGINE                                   \
70   MAKE_MLIR_PYTHON_QUALNAME("execution_engine.ExecutionEngine._CAPIPtr")
71 #define MLIR_PYTHON_CAPSULE_INTEGER_SET                                        \
72   MAKE_MLIR_PYTHON_QUALNAME("ir.IntegerSet._CAPIPtr")
73 #define MLIR_PYTHON_CAPSULE_LOCATION                                           \
74   MAKE_MLIR_PYTHON_QUALNAME("ir.Location._CAPIPtr")
75 #define MLIR_PYTHON_CAPSULE_MODULE                                             \
76   MAKE_MLIR_PYTHON_QUALNAME("ir.Module._CAPIPtr")
77 #define MLIR_PYTHON_CAPSULE_OPERATION                                          \
78   MAKE_MLIR_PYTHON_QUALNAME("ir.Operation._CAPIPtr")
79 #define MLIR_PYTHON_CAPSULE_TYPE MAKE_MLIR_PYTHON_QUALNAME("ir.Type._CAPIPtr")
80 #define MLIR_PYTHON_CAPSULE_PASS_MANAGER                                       \
81   MAKE_MLIR_PYTHON_QUALNAME("passmanager.PassManager._CAPIPtr")
82 #define MLIR_PYTHON_CAPSULE_VALUE MAKE_MLIR_PYTHON_QUALNAME("ir.Value._CAPIPtr")
83 
84 /** Attribute on MLIR Python objects that expose their C-API pointer.
85  * This will be a type-specific capsule created as per one of the helpers
86  * below.
87  *
88  * Ownership is not transferred by acquiring a capsule in this way: the
89  * validity of the pointer wrapped by the capsule will be bounded by the
90  * lifetime of the Python object that produced it. Only the name and pointer
91  * of the capsule are set. The caller is free to set a destructor and context
92  * as needed to manage anything further. */
93 #define MLIR_PYTHON_CAPI_PTR_ATTR "_CAPIPtr"
94 
95 /** Attribute on MLIR Python objects that exposes a factory function for
96  * constructing the corresponding Python object from a type-specific
97  * capsule wrapping the C-API pointer. The signature of the function is:
98  *   def _CAPICreate(capsule) -> object
99  * Calling such a function implies a transfer of ownership of the object the
100  * capsule wraps: after such a call, the capsule should be considered invalid,
101  * and its wrapped pointer must not be destroyed.
102  *
103  * Only a very small number of Python objects can be created in such a fashion
104  * (i.e. top-level types such as Context where the lifetime can be cleanly
105  * delineated). */
106 #define MLIR_PYTHON_CAPI_FACTORY_ATTR "_CAPICreate"
107 
108 /// Gets a void* from a wrapped struct. Needed because const cast is different
109 /// between C/C++.
110 #ifdef __cplusplus
111 #define MLIR_PYTHON_GET_WRAPPED_POINTER(object)                                \
112   (const_cast<void *>((object).ptr))
113 #else
114 #define MLIR_PYTHON_GET_WRAPPED_POINTER(object) (void *)(object.ptr)
115 #endif
116 
117 #ifdef __cplusplus
118 extern "C" {
119 #endif
120 
121 /** Creates a capsule object encapsulating the raw C-API MlirAffineExpr. The
122  * returned capsule does not extend or affect ownership of any Python objects
123  * that reference the expression in any way.
124  */
mlirPythonAffineExprToCapsule(MlirAffineExpr expr)125 static inline PyObject *mlirPythonAffineExprToCapsule(MlirAffineExpr expr) {
126   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(expr),
127                        MLIR_PYTHON_CAPSULE_AFFINE_EXPR, NULL);
128 }
129 
130 /** Extracts an MlirAffineExpr from a capsule as produced from
131  * mlirPythonAffineExprToCapsule. If the capsule is not of the right type, then
132  * a null expression is returned (as checked via mlirAffineExprIsNull). In such
133  * a case, the Python APIs will have already set an error. */
mlirPythonCapsuleToAffineExpr(PyObject * capsule)134 static inline MlirAffineExpr mlirPythonCapsuleToAffineExpr(PyObject *capsule) {
135   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_AFFINE_EXPR);
136   MlirAffineExpr expr = {ptr};
137   return expr;
138 }
139 
140 /** Creates a capsule object encapsulating the raw C-API MlirAttribute.
141  * The returned capsule does not extend or affect ownership of any Python
142  * objects that reference the attribute in any way.
143  */
mlirPythonAttributeToCapsule(MlirAttribute attribute)144 static inline PyObject *mlirPythonAttributeToCapsule(MlirAttribute attribute) {
145   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(attribute),
146                        MLIR_PYTHON_CAPSULE_ATTRIBUTE, NULL);
147 }
148 
149 /** Extracts an MlirAttribute from a capsule as produced from
150  * mlirPythonAttributeToCapsule. If the capsule is not of the right type, then
151  * a null attribute is returned (as checked via mlirAttributeIsNull). In such a
152  * case, the Python APIs will have already set an error. */
mlirPythonCapsuleToAttribute(PyObject * capsule)153 static inline MlirAttribute mlirPythonCapsuleToAttribute(PyObject *capsule) {
154   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_ATTRIBUTE);
155   MlirAttribute attr = {ptr};
156   return attr;
157 }
158 
159 /** Creates a capsule object encapsulating the raw C-API MlirContext.
160  * The returned capsule does not extend or affect ownership of any Python
161  * objects that reference the context in any way.
162  */
mlirPythonContextToCapsule(MlirContext context)163 static inline PyObject *mlirPythonContextToCapsule(MlirContext context) {
164   return PyCapsule_New(context.ptr, MLIR_PYTHON_CAPSULE_CONTEXT, NULL);
165 }
166 
167 /** Extracts a MlirContext from a capsule as produced from
168  * mlirPythonContextToCapsule. If the capsule is not of the right type, then
169  * a null context is returned (as checked via mlirContextIsNull). In such a
170  * case, the Python APIs will have already set an error. */
mlirPythonCapsuleToContext(PyObject * capsule)171 static inline MlirContext mlirPythonCapsuleToContext(PyObject *capsule) {
172   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_CONTEXT);
173   MlirContext context = {ptr};
174   return context;
175 }
176 
177 /** Creates a capsule object encapsulating the raw C-API MlirDialectRegistry.
178  * The returned capsule does not extend or affect ownership of any Python
179  * objects that reference the context in any way.
180  */
181 static inline PyObject *
mlirPythonDialectRegistryToCapsule(MlirDialectRegistry registry)182 mlirPythonDialectRegistryToCapsule(MlirDialectRegistry registry) {
183   return PyCapsule_New(registry.ptr, MLIR_PYTHON_CAPSULE_DIALECT_REGISTRY,
184                        NULL);
185 }
186 
187 /** Extracts an MlirDialectRegistry from a capsule as produced from
188  * mlirPythonDialectRegistryToCapsule. If the capsule is not of the right type,
189  * then a null context is returned (as checked via mlirContextIsNull). In such a
190  * case, the Python APIs will have already set an error. */
191 static inline MlirDialectRegistry
mlirPythonCapsuleToDialectRegistry(PyObject * capsule)192 mlirPythonCapsuleToDialectRegistry(PyObject *capsule) {
193   void *ptr =
194       PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_DIALECT_REGISTRY);
195   MlirDialectRegistry registry = {ptr};
196   return registry;
197 }
198 
199 /** Creates a capsule object encapsulating the raw C-API MlirLocation.
200  * The returned capsule does not extend or affect ownership of any Python
201  * objects that reference the location in any way. */
mlirPythonLocationToCapsule(MlirLocation loc)202 static inline PyObject *mlirPythonLocationToCapsule(MlirLocation loc) {
203   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(loc),
204                        MLIR_PYTHON_CAPSULE_LOCATION, NULL);
205 }
206 
207 /** Extracts an MlirLocation from a capsule as produced from
208  * mlirPythonLocationToCapsule. If the capsule is not of the right type, then
209  * a null module is returned (as checked via mlirLocationIsNull). In such a
210  * case, the Python APIs will have already set an error. */
mlirPythonCapsuleToLocation(PyObject * capsule)211 static inline MlirLocation mlirPythonCapsuleToLocation(PyObject *capsule) {
212   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_LOCATION);
213   MlirLocation loc = {ptr};
214   return loc;
215 }
216 
217 /** Creates a capsule object encapsulating the raw C-API MlirModule.
218  * The returned capsule does not extend or affect ownership of any Python
219  * objects that reference the module in any way. */
mlirPythonModuleToCapsule(MlirModule module)220 static inline PyObject *mlirPythonModuleToCapsule(MlirModule module) {
221   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(module),
222                        MLIR_PYTHON_CAPSULE_MODULE, NULL);
223 }
224 
225 /** Extracts an MlirModule from a capsule as produced from
226  * mlirPythonModuleToCapsule. If the capsule is not of the right type, then
227  * a null module is returned (as checked via mlirModuleIsNull). In such a
228  * case, the Python APIs will have already set an error. */
mlirPythonCapsuleToModule(PyObject * capsule)229 static inline MlirModule mlirPythonCapsuleToModule(PyObject *capsule) {
230   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_MODULE);
231   MlirModule module = {ptr};
232   return module;
233 }
234 
235 /** Creates a capsule object encapsulating the raw C-API MlirPassManager.
236  * The returned capsule does not extend or affect ownership of any Python
237  * objects that reference the module in any way. */
mlirPythonPassManagerToCapsule(MlirPassManager pm)238 static inline PyObject *mlirPythonPassManagerToCapsule(MlirPassManager pm) {
239   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(pm),
240                        MLIR_PYTHON_CAPSULE_PASS_MANAGER, NULL);
241 }
242 
243 /** Extracts an MlirPassManager from a capsule as produced from
244  * mlirPythonPassManagerToCapsule. If the capsule is not of the right type, then
245  * a null pass manager is returned (as checked via mlirPassManagerIsNull). */
246 static inline MlirPassManager
mlirPythonCapsuleToPassManager(PyObject * capsule)247 mlirPythonCapsuleToPassManager(PyObject *capsule) {
248   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_PASS_MANAGER);
249   MlirPassManager pm = {ptr};
250   return pm;
251 }
252 
253 /** Creates a capsule object encapsulating the raw C-API MlirOperation.
254  * The returned capsule does not extend or affect ownership of any Python
255  * objects that reference the operation in any way.
256  */
mlirPythonOperationToCapsule(MlirOperation operation)257 static inline PyObject *mlirPythonOperationToCapsule(MlirOperation operation) {
258   return PyCapsule_New(operation.ptr, MLIR_PYTHON_CAPSULE_OPERATION, NULL);
259 }
260 
261 /** Extracts an MlirOperations from a capsule as produced from
262  * mlirPythonOperationToCapsule. If the capsule is not of the right type, then
263  * a null type is returned (as checked via mlirOperationIsNull). In such a
264  * case, the Python APIs will have already set an error. */
mlirPythonCapsuleToOperation(PyObject * capsule)265 static inline MlirOperation mlirPythonCapsuleToOperation(PyObject *capsule) {
266   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_OPERATION);
267   MlirOperation op = {ptr};
268   return op;
269 }
270 
271 /** Creates a capsule object encapsulating the raw C-API MlirType.
272  * The returned capsule does not extend or affect ownership of any Python
273  * objects that reference the type in any way.
274  */
mlirPythonTypeToCapsule(MlirType type)275 static inline PyObject *mlirPythonTypeToCapsule(MlirType type) {
276   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(type),
277                        MLIR_PYTHON_CAPSULE_TYPE, NULL);
278 }
279 
280 /** Extracts an MlirType from a capsule as produced from
281  * mlirPythonTypeToCapsule. If the capsule is not of the right type, then
282  * a null type is returned (as checked via mlirTypeIsNull). In such a
283  * case, the Python APIs will have already set an error. */
mlirPythonCapsuleToType(PyObject * capsule)284 static inline MlirType mlirPythonCapsuleToType(PyObject *capsule) {
285   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_TYPE);
286   MlirType type = {ptr};
287   return type;
288 }
289 
290 /** Creates a capsule object encapsulating the raw C-API MlirAffineMap.
291  * The returned capsule does not extend or affect ownership of any Python
292  * objects that reference the type in any way.
293  */
mlirPythonAffineMapToCapsule(MlirAffineMap affineMap)294 static inline PyObject *mlirPythonAffineMapToCapsule(MlirAffineMap affineMap) {
295   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(affineMap),
296                        MLIR_PYTHON_CAPSULE_AFFINE_MAP, NULL);
297 }
298 
299 /** Extracts an MlirAffineMap from a capsule as produced from
300  * mlirPythonAffineMapToCapsule. If the capsule is not of the right type, then
301  * a null type is returned (as checked via mlirAffineMapIsNull). In such a
302  * case, the Python APIs will have already set an error. */
mlirPythonCapsuleToAffineMap(PyObject * capsule)303 static inline MlirAffineMap mlirPythonCapsuleToAffineMap(PyObject *capsule) {
304   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_AFFINE_MAP);
305   MlirAffineMap affineMap = {ptr};
306   return affineMap;
307 }
308 
309 /** Creates a capsule object encapsulating the raw C-API MlirIntegerSet.
310  * The returned capsule does not extend or affect ownership of any Python
311  * objects that reference the set in any way. */
312 static inline PyObject *
mlirPythonIntegerSetToCapsule(MlirIntegerSet integerSet)313 mlirPythonIntegerSetToCapsule(MlirIntegerSet integerSet) {
314   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(integerSet),
315                        MLIR_PYTHON_CAPSULE_INTEGER_SET, NULL);
316 }
317 
318 /** Extracts an MlirIntegerSet from a capsule as produced from
319  * mlirPythonIntegerSetToCapsule. If the capsule is not of the right type, then
320  * a null set is returned (as checked via mlirIntegerSetIsNull). In such a
321  * case, the Python APIs will have already set an error. */
mlirPythonCapsuleToIntegerSet(PyObject * capsule)322 static inline MlirIntegerSet mlirPythonCapsuleToIntegerSet(PyObject *capsule) {
323   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_INTEGER_SET);
324   MlirIntegerSet integerSet = {ptr};
325   return integerSet;
326 }
327 
328 /** Creates a capsule object encapsulating the raw C-API MlirExecutionEngine.
329  * The returned capsule does not extend or affect ownership of any Python
330  * objects that reference the set in any way. */
331 static inline PyObject *
mlirPythonExecutionEngineToCapsule(MlirExecutionEngine jit)332 mlirPythonExecutionEngineToCapsule(MlirExecutionEngine jit) {
333   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(jit),
334                        MLIR_PYTHON_CAPSULE_EXECUTION_ENGINE, NULL);
335 }
336 
337 /** Extracts an MlirExecutionEngine from a capsule as produced from
338  * mlirPythonIntegerSetToCapsule. If the capsule is not of the right type, then
339  * a null set is returned (as checked via mlirExecutionEngineIsNull). In such a
340  * case, the Python APIs will have already set an error. */
341 static inline MlirExecutionEngine
mlirPythonCapsuleToExecutionEngine(PyObject * capsule)342 mlirPythonCapsuleToExecutionEngine(PyObject *capsule) {
343   void *ptr =
344       PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_EXECUTION_ENGINE);
345   MlirExecutionEngine jit = {ptr};
346   return jit;
347 }
348 
349 /** Creates a capsule object encapsulating the raw C-API MlirValue.
350  * The returned capsule does not extend or affect ownership of any Python
351  * objects that reference the operation in any way.
352  */
mlirPythonValueToCapsule(MlirValue value)353 static inline PyObject *mlirPythonValueToCapsule(MlirValue value) {
354   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(value),
355                        MLIR_PYTHON_CAPSULE_VALUE, NULL);
356 }
357 
358 /** Extracts an MlirValue from a capsule as produced from
359  * mlirPythonValueToCapsule. If the capsule is not of the right type, then a
360  * null type is returned (as checked via mlirValueIsNull). In such a case, the
361  * Python APIs will have already set an error. */
mlirPythonCapsuleToValue(PyObject * capsule)362 static inline MlirValue mlirPythonCapsuleToValue(PyObject *capsule) {
363   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_VALUE);
364   MlirValue value = {ptr};
365   return value;
366 }
367 
368 #ifdef __cplusplus
369 }
370 #endif
371 
372 #endif // MLIR_C_BINDINGS_PYTHON_INTEROP_H
373