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_EXECUTION_ENGINE                                   \
68   MAKE_MLIR_PYTHON_QUALNAME("execution_engine.ExecutionEngine._CAPIPtr")
69 #define MLIR_PYTHON_CAPSULE_INTEGER_SET                                        \
70   MAKE_MLIR_PYTHON_QUALNAME("ir.IntegerSet._CAPIPtr")
71 #define MLIR_PYTHON_CAPSULE_LOCATION                                           \
72   MAKE_MLIR_PYTHON_QUALNAME("ir.Location._CAPIPtr")
73 #define MLIR_PYTHON_CAPSULE_MODULE                                             \
74   MAKE_MLIR_PYTHON_QUALNAME("ir.Module._CAPIPtr")
75 #define MLIR_PYTHON_CAPSULE_OPERATION                                          \
76   MAKE_MLIR_PYTHON_QUALNAME("ir.Operation._CAPIPtr")
77 #define MLIR_PYTHON_CAPSULE_TYPE MAKE_MLIR_PYTHON_QUALNAME("ir.Type._CAPIPtr")
78 #define MLIR_PYTHON_CAPSULE_PASS_MANAGER                                       \
79   MAKE_MLIR_PYTHON_QUALNAME("passmanager.PassManager._CAPIPtr")
80 #define MLIR_PYTHON_CAPSULE_VALUE MAKE_MLIR_PYTHON_QUALNAME("ir.Value._CAPIPtr")
81 
82 /** Attribute on MLIR Python objects that expose their C-API pointer.
83  * This will be a type-specific capsule created as per one of the helpers
84  * below.
85  *
86  * Ownership is not transferred by acquiring a capsule in this way: the
87  * validity of the pointer wrapped by the capsule will be bounded by the
88  * lifetime of the Python object that produced it. Only the name and pointer
89  * of the capsule are set. The caller is free to set a destructor and context
90  * as needed to manage anything further. */
91 #define MLIR_PYTHON_CAPI_PTR_ATTR "_CAPIPtr"
92 
93 /** Attribute on MLIR Python objects that exposes a factory function for
94  * constructing the corresponding Python object from a type-specific
95  * capsule wrapping the C-API pointer. The signature of the function is:
96  *   def _CAPICreate(capsule) -> object
97  * Calling such a function implies a transfer of ownership of the object the
98  * capsule wraps: after such a call, the capsule should be considered invalid,
99  * and its wrapped pointer must not be destroyed.
100  *
101  * Only a very small number of Python objects can be created in such a fashion
102  * (i.e. top-level types such as Context where the lifetime can be cleanly
103  * delineated). */
104 #define MLIR_PYTHON_CAPI_FACTORY_ATTR "_CAPICreate"
105 
106 /// Gets a void* from a wrapped struct. Needed because const cast is different
107 /// between C/C++.
108 #ifdef __cplusplus
109 #define MLIR_PYTHON_GET_WRAPPED_POINTER(object)                                \
110   (const_cast<void *>((object).ptr))
111 #else
112 #define MLIR_PYTHON_GET_WRAPPED_POINTER(object) (void *)(object.ptr)
113 #endif
114 
115 #ifdef __cplusplus
116 extern "C" {
117 #endif
118 
119 /** Creates a capsule object encapsulating the raw C-API MlirAffineExpr. The
120  * returned capsule does not extend or affect ownership of any Python objects
121  * that reference the expression in any way.
122  */
123 static inline PyObject *mlirPythonAffineExprToCapsule(MlirAffineExpr expr) {
124   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(expr),
125                        MLIR_PYTHON_CAPSULE_AFFINE_EXPR, NULL);
126 }
127 
128 /** Extracts an MlirAffineExpr from a capsule as produced from
129  * mlirPythonAffineExprToCapsule. If the capsule is not of the right type, then
130  * a null expression is returned (as checked via mlirAffineExprIsNull). In such
131  * a case, the Python APIs will have already set an error. */
132 static inline MlirAffineExpr mlirPythonCapsuleToAffineExpr(PyObject *capsule) {
133   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_AFFINE_EXPR);
134   MlirAffineExpr expr = {ptr};
135   return expr;
136 }
137 
138 /** Creates a capsule object encapsulating the raw C-API MlirAttribute.
139  * The returned capsule does not extend or affect ownership of any Python
140  * objects that reference the attribute in any way.
141  */
142 static inline PyObject *mlirPythonAttributeToCapsule(MlirAttribute attribute) {
143   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(attribute),
144                        MLIR_PYTHON_CAPSULE_ATTRIBUTE, NULL);
145 }
146 
147 /** Extracts an MlirAttribute from a capsule as produced from
148  * mlirPythonAttributeToCapsule. If the capsule is not of the right type, then
149  * a null attribute is returned (as checked via mlirAttributeIsNull). In such a
150  * case, the Python APIs will have already set an error. */
151 static inline MlirAttribute mlirPythonCapsuleToAttribute(PyObject *capsule) {
152   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_ATTRIBUTE);
153   MlirAttribute attr = {ptr};
154   return attr;
155 }
156 
157 /** Creates a capsule object encapsulating the raw C-API MlirContext.
158  * The returned capsule does not extend or affect ownership of any Python
159  * objects that reference the context in any way.
160  */
161 static inline PyObject *mlirPythonContextToCapsule(MlirContext context) {
162   return PyCapsule_New(context.ptr, MLIR_PYTHON_CAPSULE_CONTEXT, NULL);
163 }
164 
165 /** Extracts a MlirContext from a capsule as produced from
166  * mlirPythonContextToCapsule. If the capsule is not of the right type, then
167  * a null context is returned (as checked via mlirContextIsNull). In such a
168  * case, the Python APIs will have already set an error. */
169 static inline MlirContext mlirPythonCapsuleToContext(PyObject *capsule) {
170   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_CONTEXT);
171   MlirContext context = {ptr};
172   return context;
173 }
174 
175 /** Creates a capsule object encapsulating the raw C-API MlirLocation.
176  * The returned capsule does not extend or affect ownership of any Python
177  * objects that reference the location in any way. */
178 static inline PyObject *mlirPythonLocationToCapsule(MlirLocation loc) {
179   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(loc),
180                        MLIR_PYTHON_CAPSULE_LOCATION, NULL);
181 }
182 
183 /** Extracts an MlirLocation from a capsule as produced from
184  * mlirPythonLocationToCapsule. If the capsule is not of the right type, then
185  * a null module is returned (as checked via mlirLocationIsNull). In such a
186  * case, the Python APIs will have already set an error. */
187 static inline MlirLocation mlirPythonCapsuleToLocation(PyObject *capsule) {
188   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_LOCATION);
189   MlirLocation loc = {ptr};
190   return loc;
191 }
192 
193 /** Creates a capsule object encapsulating the raw C-API MlirModule.
194  * The returned capsule does not extend or affect ownership of any Python
195  * objects that reference the module in any way. */
196 static inline PyObject *mlirPythonModuleToCapsule(MlirModule module) {
197   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(module),
198                        MLIR_PYTHON_CAPSULE_MODULE, NULL);
199 }
200 
201 /** Extracts an MlirModule from a capsule as produced from
202  * mlirPythonModuleToCapsule. If the capsule is not of the right type, then
203  * a null module is returned (as checked via mlirModuleIsNull). In such a
204  * case, the Python APIs will have already set an error. */
205 static inline MlirModule mlirPythonCapsuleToModule(PyObject *capsule) {
206   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_MODULE);
207   MlirModule module = {ptr};
208   return module;
209 }
210 
211 /** Creates a capsule object encapsulating the raw C-API MlirPassManager.
212  * The returned capsule does not extend or affect ownership of any Python
213  * objects that reference the module in any way. */
214 static inline PyObject *mlirPythonPassManagerToCapsule(MlirPassManager pm) {
215   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(pm),
216                        MLIR_PYTHON_CAPSULE_PASS_MANAGER, NULL);
217 }
218 
219 /** Extracts an MlirPassManager from a capsule as produced from
220  * mlirPythonPassManagerToCapsule. If the capsule is not of the right type, then
221  * a null pass manager is returned (as checked via mlirPassManagerIsNull). */
222 static inline MlirPassManager
223 mlirPythonCapsuleToPassManager(PyObject *capsule) {
224   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_PASS_MANAGER);
225   MlirPassManager pm = {ptr};
226   return pm;
227 }
228 
229 /** Creates a capsule object encapsulating the raw C-API MlirOperation.
230  * The returned capsule does not extend or affect ownership of any Python
231  * objects that reference the operation in any way.
232  */
233 static inline PyObject *mlirPythonOperationToCapsule(MlirOperation operation) {
234   return PyCapsule_New(operation.ptr, MLIR_PYTHON_CAPSULE_OPERATION, NULL);
235 }
236 
237 /** Extracts an MlirOperations from a capsule as produced from
238  * mlirPythonOperationToCapsule. If the capsule is not of the right type, then
239  * a null type is returned (as checked via mlirOperationIsNull). In such a
240  * case, the Python APIs will have already set an error. */
241 static inline MlirOperation mlirPythonCapsuleToOperation(PyObject *capsule) {
242   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_OPERATION);
243   MlirOperation op = {ptr};
244   return op;
245 }
246 
247 /** Creates a capsule object encapsulating the raw C-API MlirType.
248  * The returned capsule does not extend or affect ownership of any Python
249  * objects that reference the type in any way.
250  */
251 static inline PyObject *mlirPythonTypeToCapsule(MlirType type) {
252   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(type),
253                        MLIR_PYTHON_CAPSULE_TYPE, NULL);
254 }
255 
256 /** Extracts an MlirType from a capsule as produced from
257  * mlirPythonTypeToCapsule. If the capsule is not of the right type, then
258  * a null type is returned (as checked via mlirTypeIsNull). In such a
259  * case, the Python APIs will have already set an error. */
260 static inline MlirType mlirPythonCapsuleToType(PyObject *capsule) {
261   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_TYPE);
262   MlirType type = {ptr};
263   return type;
264 }
265 
266 /** Creates a capsule object encapsulating the raw C-API MlirAffineMap.
267  * The returned capsule does not extend or affect ownership of any Python
268  * objects that reference the type in any way.
269  */
270 static inline PyObject *mlirPythonAffineMapToCapsule(MlirAffineMap affineMap) {
271   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(affineMap),
272                        MLIR_PYTHON_CAPSULE_AFFINE_MAP, NULL);
273 }
274 
275 /** Extracts an MlirAffineMap from a capsule as produced from
276  * mlirPythonAffineMapToCapsule. If the capsule is not of the right type, then
277  * a null type is returned (as checked via mlirAffineMapIsNull). In such a
278  * case, the Python APIs will have already set an error. */
279 static inline MlirAffineMap mlirPythonCapsuleToAffineMap(PyObject *capsule) {
280   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_AFFINE_MAP);
281   MlirAffineMap affineMap = {ptr};
282   return affineMap;
283 }
284 
285 /** Creates a capsule object encapsulating the raw C-API MlirIntegerSet.
286  * The returned capsule does not extend or affect ownership of any Python
287  * objects that reference the set in any way. */
288 static inline PyObject *
289 mlirPythonIntegerSetToCapsule(MlirIntegerSet integerSet) {
290   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(integerSet),
291                        MLIR_PYTHON_CAPSULE_INTEGER_SET, NULL);
292 }
293 
294 /** Extracts an MlirIntegerSet from a capsule as produced from
295  * mlirPythonIntegerSetToCapsule. If the capsule is not of the right type, then
296  * a null set is returned (as checked via mlirIntegerSetIsNull). In such a
297  * case, the Python APIs will have already set an error. */
298 static inline MlirIntegerSet mlirPythonCapsuleToIntegerSet(PyObject *capsule) {
299   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_INTEGER_SET);
300   MlirIntegerSet integerSet = {ptr};
301   return integerSet;
302 }
303 
304 /** Creates a capsule object encapsulating the raw C-API MlirExecutionEngine.
305  * The returned capsule does not extend or affect ownership of any Python
306  * objects that reference the set in any way. */
307 static inline PyObject *
308 mlirPythonExecutionEngineToCapsule(MlirExecutionEngine jit) {
309   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(jit),
310                        MLIR_PYTHON_CAPSULE_EXECUTION_ENGINE, NULL);
311 }
312 
313 /** Extracts an MlirExecutionEngine from a capsule as produced from
314  * mlirPythonIntegerSetToCapsule. If the capsule is not of the right type, then
315  * a null set is returned (as checked via mlirExecutionEngineIsNull). In such a
316  * case, the Python APIs will have already set an error. */
317 static inline MlirExecutionEngine
318 mlirPythonCapsuleToExecutionEngine(PyObject *capsule) {
319   void *ptr =
320       PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_EXECUTION_ENGINE);
321   MlirExecutionEngine jit = {ptr};
322   return jit;
323 }
324 
325 /** Creates a capsule object encapsulating the raw C-API MlirValue.
326  * The returned capsule does not extend or affect ownership of any Python
327  * objects that reference the operation in any way.
328  */
329 static inline PyObject *mlirPythonValueToCapsule(MlirValue value) {
330   return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(value),
331                        MLIR_PYTHON_CAPSULE_VALUE, NULL);
332 }
333 
334 /** Extracts an MlirValue from a capsule as produced from
335  * mlirPythonValueToCapsule. If the capsule is not of the right type, then a
336  * null type is returned (as checked via mlirValueIsNull). In such a case, the
337  * Python APIs will have already set an error. */
338 static inline MlirValue mlirPythonCapsuleToValue(PyObject *capsule) {
339   void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_VALUE);
340   MlirValue value = {ptr};
341   return value;
342 }
343 
344 #ifdef __cplusplus
345 }
346 #endif
347 
348 #endif // MLIR_C_BINDINGS_PYTHON_INTEROP_H
349