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_BLOCK MAKE_MLIR_PYTHON_QUALNAME("ir.Block._CAPIPtr")
66#define MLIR_PYTHON_CAPSULE_CONTEXT \
67 MAKE_MLIR_PYTHON_QUALNAME("ir.Context._CAPIPtr")
68#define MLIR_PYTHON_CAPSULE_DIALECT_REGISTRY \
69 MAKE_MLIR_PYTHON_QUALNAME("ir.DialectRegistry._CAPIPtr")
70#define MLIR_PYTHON_CAPSULE_EXECUTION_ENGINE \
71 MAKE_MLIR_PYTHON_QUALNAME("execution_engine.ExecutionEngine._CAPIPtr")
72#define MLIR_PYTHON_CAPSULE_INTEGER_SET \
73 MAKE_MLIR_PYTHON_QUALNAME("ir.IntegerSet._CAPIPtr")
74#define MLIR_PYTHON_CAPSULE_LOCATION \
75 MAKE_MLIR_PYTHON_QUALNAME("ir.Location._CAPIPtr")
76#define MLIR_PYTHON_CAPSULE_MODULE \
77 MAKE_MLIR_PYTHON_QUALNAME("ir.Module._CAPIPtr")
78#define MLIR_PYTHON_CAPSULE_OPERATION \
79 MAKE_MLIR_PYTHON_QUALNAME("ir.Operation._CAPIPtr")
80#define MLIR_PYTHON_CAPSULE_TYPE MAKE_MLIR_PYTHON_QUALNAME("ir.Type._CAPIPtr")
81#define MLIR_PYTHON_CAPSULE_PASS_MANAGER \
82 MAKE_MLIR_PYTHON_QUALNAME("passmanager.PassManager._CAPIPtr")
83#define MLIR_PYTHON_CAPSULE_VALUE MAKE_MLIR_PYTHON_QUALNAME("ir.Value._CAPIPtr")
84#define MLIR_PYTHON_CAPSULE_TYPEID \
85 MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID._CAPIPtr")
86
87/** Attribute on MLIR Python objects that expose their C-API pointer.
88 * This will be a type-specific capsule created as per one of the helpers
89 * below.
90 *
91 * Ownership is not transferred by acquiring a capsule in this way: the
92 * validity of the pointer wrapped by the capsule will be bounded by the
93 * lifetime of the Python object that produced it. Only the name and pointer
94 * of the capsule are set. The caller is free to set a destructor and context
95 * as needed to manage anything further. */
96#define MLIR_PYTHON_CAPI_PTR_ATTR "_CAPIPtr"
97
98/** Attribute on MLIR Python objects that exposes a factory function for
99 * constructing the corresponding Python object from a type-specific
100 * capsule wrapping the C-API pointer. The signature of the function is:
101 * def _CAPICreate(capsule) -> object
102 * Calling such a function implies a transfer of ownership of the object the
103 * capsule wraps: after such a call, the capsule should be considered invalid,
104 * and its wrapped pointer must not be destroyed.
105 *
106 * Only a very small number of Python objects can be created in such a fashion
107 * (i.e. top-level types such as Context where the lifetime can be cleanly
108 * delineated). */
109#define MLIR_PYTHON_CAPI_FACTORY_ATTR "_CAPICreate"
110
111/** Attribute on MLIR Python objects that expose a function for downcasting the
112 * corresponding Python object to a subclass if the object is in fact a subclass
113 * (Concrete or mlir_type_subclass) of ir.Type. The signature of the function
114 * is: def maybe_downcast(self) -> object where the resulting object will
115 * (possibly) be an instance of the subclass.
116 */
117#define MLIR_PYTHON_MAYBE_DOWNCAST_ATTR "maybe_downcast"
118
119/** Attribute on main C extension module (_mlir) that corresponds to the
120 * type caster registration binding. The signature of the function is:
121 * def register_type_caster(MlirTypeID mlirTypeID, *, bool replace)
122 * which then takes a typeCaster (register_type_caster is meant to be used as a
123 * decorator from python), and where replace indicates the typeCaster should
124 * replace any existing registered type casters (such as those for upstream
125 * ConcreteTypes). The interface of the typeCaster is: def type_caster(ir.Type)
126 * -> SubClassTypeT where SubClassTypeT indicates the result should be a
127 * subclass (inherit from) ir.Type.
128 */
129#define MLIR_PYTHON_CAPI_TYPE_CASTER_REGISTER_ATTR "register_type_caster"
130
131/** Attribute on main C extension module (_mlir) that corresponds to the
132 * value caster registration binding. The signature of the function is:
133 * def register_value_caster(MlirTypeID mlirTypeID, *, bool replace)
134 * which then takes a valueCaster (register_value_caster is meant to be used as
135 * a decorator, from python), and where replace indicates the valueCaster should
136 * replace any existing registered value casters. The interface of the
137 * valueCaster is: def value_caster(ir.Value) -> SubClassValueT where
138 * SubClassValueT indicates the result should be a subclass (inherit from)
139 * ir.Value.
140 */
141#define MLIR_PYTHON_CAPI_VALUE_CASTER_REGISTER_ATTR "register_value_caster"
142
143/// Gets a void* from a wrapped struct. Needed because const cast is different
144/// between C/C++.
145#ifdef __cplusplus
146#define MLIR_PYTHON_GET_WRAPPED_POINTER(object) \
147 (const_cast<void *>((object).ptr))
148#else
149#define MLIR_PYTHON_GET_WRAPPED_POINTER(object) (void *)(object.ptr)
150#endif
151
152#ifdef __cplusplus
153extern "C" {
154#endif
155
156/** Creates a capsule object encapsulating the raw C-API MlirAffineExpr. The
157 * returned capsule does not extend or affect ownership of any Python objects
158 * that reference the expression in any way.
159 */
160static inline PyObject *mlirPythonAffineExprToCapsule(MlirAffineExpr expr) {
161 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(expr),
162 MLIR_PYTHON_CAPSULE_AFFINE_EXPR, NULL);
163}
164
165/** Extracts an MlirAffineExpr from a capsule as produced from
166 * mlirPythonAffineExprToCapsule. If the capsule is not of the right type, then
167 * a null expression is returned (as checked via mlirAffineExprIsNull). In such
168 * a case, the Python APIs will have already set an error. */
169static inline MlirAffineExpr mlirPythonCapsuleToAffineExpr(PyObject *capsule) {
170 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_AFFINE_EXPR);
171 MlirAffineExpr expr = {.ptr: ptr};
172 return expr;
173}
174
175/** Creates a capsule object encapsulating the raw C-API MlirAttribute.
176 * The returned capsule does not extend or affect ownership of any Python
177 * objects that reference the attribute in any way.
178 */
179static inline PyObject *mlirPythonAttributeToCapsule(MlirAttribute attribute) {
180 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(attribute),
181 MLIR_PYTHON_CAPSULE_ATTRIBUTE, NULL);
182}
183
184/** Extracts an MlirAttribute from a capsule as produced from
185 * mlirPythonAttributeToCapsule. If the capsule is not of the right type, then
186 * a null attribute is returned (as checked via mlirAttributeIsNull). In such a
187 * case, the Python APIs will have already set an error. */
188static inline MlirAttribute mlirPythonCapsuleToAttribute(PyObject *capsule) {
189 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_ATTRIBUTE);
190 MlirAttribute attr = {.ptr: ptr};
191 return attr;
192}
193
194/** Creates a capsule object encapsulating the raw C-API MlirBlock.
195 * The returned capsule does not extend or affect ownership of any Python
196 * objects that reference the module in any way. */
197static inline PyObject *mlirPythonBlockToCapsule(MlirBlock block) {
198 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(block),
199 MLIR_PYTHON_CAPSULE_BLOCK, NULL);
200}
201
202/** Extracts an MlirBlock from a capsule as produced from
203 * mlirPythonBlockToCapsule. If the capsule is not of the right type, then
204 * a null pass manager is returned (as checked via mlirBlockIsNull). */
205static inline MlirBlock mlirPythonCapsuleToBlock(PyObject *capsule) {
206 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_BLOCK);
207 MlirBlock block = {.ptr: ptr};
208 return block;
209}
210
211/** Creates a capsule object encapsulating the raw C-API MlirContext.
212 * The returned capsule does not extend or affect ownership of any Python
213 * objects that reference the context in any way.
214 */
215static inline PyObject *mlirPythonContextToCapsule(MlirContext context) {
216 return PyCapsule_New(context.ptr, MLIR_PYTHON_CAPSULE_CONTEXT, NULL);
217}
218
219/** Extracts a MlirContext from a capsule as produced from
220 * mlirPythonContextToCapsule. If the capsule is not of the right type, then
221 * a null context is returned (as checked via mlirContextIsNull). In such a
222 * case, the Python APIs will have already set an error. */
223static inline MlirContext mlirPythonCapsuleToContext(PyObject *capsule) {
224 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_CONTEXT);
225 MlirContext context = {.ptr: ptr};
226 return context;
227}
228
229/** Creates a capsule object encapsulating the raw C-API MlirDialectRegistry.
230 * The returned capsule does not extend or affect ownership of any Python
231 * objects that reference the context in any way.
232 */
233static inline PyObject *
234mlirPythonDialectRegistryToCapsule(MlirDialectRegistry registry) {
235 return PyCapsule_New(registry.ptr, MLIR_PYTHON_CAPSULE_DIALECT_REGISTRY,
236 NULL);
237}
238
239/** Extracts an MlirDialectRegistry from a capsule as produced from
240 * mlirPythonDialectRegistryToCapsule. If the capsule is not of the right type,
241 * then a null context is returned (as checked via mlirContextIsNull). In such a
242 * case, the Python APIs will have already set an error. */
243static inline MlirDialectRegistry
244mlirPythonCapsuleToDialectRegistry(PyObject *capsule) {
245 void *ptr =
246 PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_DIALECT_REGISTRY);
247 MlirDialectRegistry registry = {.ptr: ptr};
248 return registry;
249}
250
251/** Creates a capsule object encapsulating the raw C-API MlirLocation.
252 * The returned capsule does not extend or affect ownership of any Python
253 * objects that reference the location in any way. */
254static inline PyObject *mlirPythonLocationToCapsule(MlirLocation loc) {
255 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(loc),
256 MLIR_PYTHON_CAPSULE_LOCATION, NULL);
257}
258
259/** Extracts an MlirLocation from a capsule as produced from
260 * mlirPythonLocationToCapsule. If the capsule is not of the right type, then
261 * a null module is returned (as checked via mlirLocationIsNull). In such a
262 * case, the Python APIs will have already set an error. */
263static inline MlirLocation mlirPythonCapsuleToLocation(PyObject *capsule) {
264 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_LOCATION);
265 MlirLocation loc = {.ptr: ptr};
266 return loc;
267}
268
269/** Creates a capsule object encapsulating the raw C-API MlirModule.
270 * The returned capsule does not extend or affect ownership of any Python
271 * objects that reference the module in any way. */
272static inline PyObject *mlirPythonModuleToCapsule(MlirModule module) {
273 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(module),
274 MLIR_PYTHON_CAPSULE_MODULE, NULL);
275}
276
277/** Extracts an MlirModule from a capsule as produced from
278 * mlirPythonModuleToCapsule. If the capsule is not of the right type, then
279 * a null module is returned (as checked via mlirModuleIsNull). In such a
280 * case, the Python APIs will have already set an error. */
281static inline MlirModule mlirPythonCapsuleToModule(PyObject *capsule) {
282 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_MODULE);
283 MlirModule module = {.ptr: ptr};
284 return module;
285}
286
287/** Creates a capsule object encapsulating the raw C-API MlirPassManager.
288 * The returned capsule does not extend or affect ownership of any Python
289 * objects that reference the module in any way. */
290static inline PyObject *mlirPythonPassManagerToCapsule(MlirPassManager pm) {
291 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(pm),
292 MLIR_PYTHON_CAPSULE_PASS_MANAGER, NULL);
293}
294
295/** Extracts an MlirPassManager from a capsule as produced from
296 * mlirPythonPassManagerToCapsule. If the capsule is not of the right type, then
297 * a null pass manager is returned (as checked via mlirPassManagerIsNull). */
298static inline MlirPassManager
299mlirPythonCapsuleToPassManager(PyObject *capsule) {
300 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_PASS_MANAGER);
301 MlirPassManager pm = {.ptr: ptr};
302 return pm;
303}
304
305/** Creates a capsule object encapsulating the raw C-API MlirOperation.
306 * The returned capsule does not extend or affect ownership of any Python
307 * objects that reference the operation in any way.
308 */
309static inline PyObject *mlirPythonOperationToCapsule(MlirOperation operation) {
310 return PyCapsule_New(operation.ptr, MLIR_PYTHON_CAPSULE_OPERATION, NULL);
311}
312
313/** Extracts an MlirOperations from a capsule as produced from
314 * mlirPythonOperationToCapsule. If the capsule is not of the right type, then
315 * a null type is returned (as checked via mlirOperationIsNull). In such a
316 * case, the Python APIs will have already set an error. */
317static inline MlirOperation mlirPythonCapsuleToOperation(PyObject *capsule) {
318 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_OPERATION);
319 MlirOperation op = {.ptr: ptr};
320 return op;
321}
322
323/** Creates a capsule object encapsulating the raw C-API MlirTypeID.
324 * The returned capsule does not extend or affect ownership of any Python
325 * objects that reference the type in any way.
326 */
327static inline PyObject *mlirPythonTypeIDToCapsule(MlirTypeID typeID) {
328 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(typeID),
329 MLIR_PYTHON_CAPSULE_TYPEID, NULL);
330}
331
332/** Extracts an MlirTypeID from a capsule as produced from
333 * mlirPythonTypeIDToCapsule. If the capsule is not of the right type, then
334 * a null type is returned (as checked via mlirTypeIDIsNull). In such a
335 * case, the Python APIs will have already set an error. */
336static inline MlirTypeID mlirPythonCapsuleToTypeID(PyObject *capsule) {
337 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_TYPEID);
338 MlirTypeID typeID = {.ptr: ptr};
339 return typeID;
340}
341
342/** Creates a capsule object encapsulating the raw C-API MlirType.
343 * The returned capsule does not extend or affect ownership of any Python
344 * objects that reference the type in any way.
345 */
346static inline PyObject *mlirPythonTypeToCapsule(MlirType type) {
347 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(type),
348 MLIR_PYTHON_CAPSULE_TYPE, NULL);
349}
350
351/** Extracts an MlirType from a capsule as produced from
352 * mlirPythonTypeToCapsule. If the capsule is not of the right type, then
353 * a null type is returned (as checked via mlirTypeIsNull). In such a
354 * case, the Python APIs will have already set an error. */
355static inline MlirType mlirPythonCapsuleToType(PyObject *capsule) {
356 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_TYPE);
357 MlirType type = {.ptr: ptr};
358 return type;
359}
360
361/** Creates a capsule object encapsulating the raw C-API MlirAffineMap.
362 * The returned capsule does not extend or affect ownership of any Python
363 * objects that reference the type in any way.
364 */
365static inline PyObject *mlirPythonAffineMapToCapsule(MlirAffineMap affineMap) {
366 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(affineMap),
367 MLIR_PYTHON_CAPSULE_AFFINE_MAP, NULL);
368}
369
370/** Extracts an MlirAffineMap from a capsule as produced from
371 * mlirPythonAffineMapToCapsule. If the capsule is not of the right type, then
372 * a null type is returned (as checked via mlirAffineMapIsNull). In such a
373 * case, the Python APIs will have already set an error. */
374static inline MlirAffineMap mlirPythonCapsuleToAffineMap(PyObject *capsule) {
375 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_AFFINE_MAP);
376 MlirAffineMap affineMap = {.ptr: ptr};
377 return affineMap;
378}
379
380/** Creates a capsule object encapsulating the raw C-API MlirIntegerSet.
381 * The returned capsule does not extend or affect ownership of any Python
382 * objects that reference the set in any way. */
383static inline PyObject *
384mlirPythonIntegerSetToCapsule(MlirIntegerSet integerSet) {
385 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(integerSet),
386 MLIR_PYTHON_CAPSULE_INTEGER_SET, NULL);
387}
388
389/** Extracts an MlirIntegerSet from a capsule as produced from
390 * mlirPythonIntegerSetToCapsule. If the capsule is not of the right type, then
391 * a null set is returned (as checked via mlirIntegerSetIsNull). In such a
392 * case, the Python APIs will have already set an error. */
393static inline MlirIntegerSet mlirPythonCapsuleToIntegerSet(PyObject *capsule) {
394 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_INTEGER_SET);
395 MlirIntegerSet integerSet = {.ptr: ptr};
396 return integerSet;
397}
398
399/** Creates a capsule object encapsulating the raw C-API MlirExecutionEngine.
400 * The returned capsule does not extend or affect ownership of any Python
401 * objects that reference the set in any way. */
402static inline PyObject *
403mlirPythonExecutionEngineToCapsule(MlirExecutionEngine jit) {
404 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(jit),
405 MLIR_PYTHON_CAPSULE_EXECUTION_ENGINE, NULL);
406}
407
408/** Extracts an MlirExecutionEngine from a capsule as produced from
409 * mlirPythonIntegerSetToCapsule. If the capsule is not of the right type, then
410 * a null set is returned (as checked via mlirExecutionEngineIsNull). In such a
411 * case, the Python APIs will have already set an error. */
412static inline MlirExecutionEngine
413mlirPythonCapsuleToExecutionEngine(PyObject *capsule) {
414 void *ptr =
415 PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_EXECUTION_ENGINE);
416 MlirExecutionEngine jit = {.ptr: ptr};
417 return jit;
418}
419
420/** Creates a capsule object encapsulating the raw C-API MlirValue.
421 * The returned capsule does not extend or affect ownership of any Python
422 * objects that reference the operation in any way.
423 */
424static inline PyObject *mlirPythonValueToCapsule(MlirValue value) {
425 return PyCapsule_New(MLIR_PYTHON_GET_WRAPPED_POINTER(value),
426 MLIR_PYTHON_CAPSULE_VALUE, NULL);
427}
428
429/** Extracts an MlirValue from a capsule as produced from
430 * mlirPythonValueToCapsule. If the capsule is not of the right type, then a
431 * null type is returned (as checked via mlirValueIsNull). In such a case, the
432 * Python APIs will have already set an error. */
433static inline MlirValue mlirPythonCapsuleToValue(PyObject *capsule) {
434 void *ptr = PyCapsule_GetPointer(capsule, MLIR_PYTHON_CAPSULE_VALUE);
435 MlirValue value = {.ptr: ptr};
436 return value;
437}
438
439#ifdef __cplusplus
440}
441#endif
442
443#endif // MLIR_C_BINDINGS_PYTHON_INTEROP_H
444

source code of mlir/include/mlir-c/Bindings/Python/Interop.h