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