-
Notifications
You must be signed in to change notification settings - Fork 322
/
Copy pathMLIRPybindAdaptors.h
326 lines (295 loc) · 11.3 KB
/
MLIRPybindAdaptors.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
//===- MLIRPybindAdaptors.h - Adaptors for interop with MLIR APIs ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains adaptors for out of tree dialects to use and extend the
// MLIR Python API via CAPI types.
//
// It is being developed out of tree but should be considered a candidate to
// move upstream once stable, so that everyone can use it.
//===----------------------------------------------------------------------===//
#ifndef CIRCT_BINDINGS_PYTHON_MLIRCAPIADAPTORS_H
#define CIRCT_BINDINGS_PYTHON_MLIRCAPIADAPTORS_H
#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>
#include <pybind11/stl.h>
#include "mlir-c/Bindings/Python/Interop.h"
#include "mlir-c/IR.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/Twine.h"
namespace py = pybind11;
// TODO: Move this to Interop.h and make it externally configurable/use it
// consistently to locate the "import mlir" top-level.
#define MLIR_PYTHON_PACKAGE_PREFIX "mlir."
// Raw CAPI type casters need to be declared before use, so always include them
// first.
namespace pybind11 {
namespace detail {
template <typename T>
struct type_caster<llvm::Optional<T>> : optional_caster<llvm::Optional<T>> {};
/// Helper to convert a presumed MLIR API object to a capsule, accepting either
/// an explicit Capsule (which can happen when two C APIs are communicating
/// directly via Python) or indirectly by querying the MLIR_PYTHON_CAPI_PTR_ATTR
/// attribute (through which supported MLIR Python API objects export their
/// contained API pointer as a capsule). This is intended to be used from
/// type casters, which are invoked with a raw handle (unowned). The returned
/// object's lifetime may not extend beyond the apiObject handle without
/// explicitly having its refcount increased (i.e. on return).
static py::object mlirApiObjectToCapsule(py::handle apiObject) {
if (PyCapsule_CheckExact(apiObject.ptr()))
return py::reinterpret_borrow<py::object>(apiObject);
return apiObject.attr(MLIR_PYTHON_CAPI_PTR_ATTR);
}
// Note: Currently all of the following support cast from py::object to the
// Mlir* C-API type, but only a few light-weight, context-bound ones
// implicitly cast the other way because the use case has not yet emerged and
// ownership is unclear.
/// Casts object -> MlirAttribute.
template <>
struct type_caster<MlirAttribute> {
PYBIND11_TYPE_CASTER(MlirAttribute, _("MlirAttribute"));
bool load(handle src, bool) {
auto capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToAttribute(capsule.ptr());
if (mlirAttributeIsNull(value)) {
return false;
}
return true;
}
static handle cast(MlirAttribute v, return_value_policy, handle) {
auto capsule =
py::reinterpret_steal<py::object>(mlirPythonAttributeToCapsule(v));
return py::module::import("mlir.ir")
.attr("Attribute")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
}
};
/// Casts object -> MlirContext.
template <>
struct type_caster<MlirContext> {
PYBIND11_TYPE_CASTER(MlirContext, _("MlirContext"));
bool load(handle src, bool) {
auto capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToContext(capsule.ptr());
if (mlirContextIsNull(value)) {
return false;
}
return true;
}
};
/// Casts object -> MlirLocation.
template <>
struct type_caster<MlirLocation> {
PYBIND11_TYPE_CASTER(MlirLocation, _("MlirLocation"));
bool load(handle src, bool) {
auto capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToLocation(capsule.ptr());
if (mlirLocationIsNull(value)) {
return false;
}
return true;
}
static handle cast(MlirLocation v, return_value_policy, handle) {
auto capsule =
py::reinterpret_steal<py::object>(mlirPythonLocationToCapsule(v));
return py::module::import("mlir.ir")
.attr("Location")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
}
};
/// Casts object -> MlirModule.
template <>
struct type_caster<MlirModule> {
PYBIND11_TYPE_CASTER(MlirModule, _("MlirModule"));
bool load(handle src, bool) {
auto capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToModule(capsule.ptr());
if (mlirModuleIsNull(value)) {
return false;
}
return true;
}
static handle cast(MlirModule v, return_value_policy, handle) {
auto capsule =
py::reinterpret_steal<py::object>(mlirPythonModuleToCapsule(v));
return py::module::import("mlir.ir")
.attr("Module")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
};
};
/// Casts object <-> MlirOperation.
template <>
struct type_caster<MlirOperation> {
PYBIND11_TYPE_CASTER(MlirOperation, _("MlirOperation"));
bool load(handle src, bool) {
auto capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToOperation(capsule.ptr());
if (mlirOperationIsNull(value)) {
return false;
}
return true;
}
static handle cast(MlirOperation v, return_value_policy, handle) {
if (v.ptr == nullptr)
return py::none();
auto capsule =
py::reinterpret_steal<py::object>(mlirPythonOperationToCapsule(v));
return py::module::import("mlir.ir")
.attr("Operation")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
};
};
/// Casts object -> MlirPassManager.
template <>
struct type_caster<MlirPassManager> {
PYBIND11_TYPE_CASTER(MlirPassManager, _("MlirPassManager"));
bool load(handle src, bool) {
auto capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToPassManager(capsule.ptr());
if (mlirPassManagerIsNull(value)) {
return false;
}
return true;
}
};
/// Casts object -> MlirType.
template <>
struct type_caster<MlirType> {
PYBIND11_TYPE_CASTER(MlirType, _("MlirType"));
bool load(handle src, bool) {
auto capsule = mlirApiObjectToCapsule(src);
value = mlirPythonCapsuleToType(capsule.ptr());
if (mlirTypeIsNull(value)) {
return false;
}
return true;
}
static handle cast(MlirType t, return_value_policy, handle) {
auto capsule =
py::reinterpret_steal<py::object>(mlirPythonTypeToCapsule(t));
return py::module::import("mlir.ir")
.attr("Type")
.attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
.release();
}
};
} // namespace detail
} // namespace pybind11
namespace mlir {
namespace python {
namespace adaptors {
/// Provides a facility like py::class_ for defining a new class in a scope,
/// but this allows extension of an arbitrary Python class, defining methods
/// on it is a similar way. Classes defined in this way are very similar to
/// if defined in Python in the usual way but use Pybind11 machinery to do
/// it. These are not "real" Pybind11 classes but pure Python classes with no
/// relation to a concrete C++ class.
///
/// Derived from a discussion upstream:
/// https://github.com/pybind/pybind11/issues/1193
/// (plus a fair amount of extra curricular poking)
/// TODO: If this proves useful, see about including it in pybind11.
class pure_subclass {
public:
struct super_class_info {
const char *className;
const char *moduleName = MLIR_PYTHON_PACKAGE_PREFIX "ir";
};
pure_subclass(py::handle scope, const char *derivedClassName,
super_class_info superClassInfo) {
superClass = py::module::import(superClassInfo.moduleName)
.attr(superClassInfo.className);
py::object pyType =
py::reinterpret_borrow<py::object>((PyObject *)&PyType_Type);
py::object metaclass = pyType(superClass);
py::dict attributes;
thisClass =
metaclass(derivedClassName, py::make_tuple(superClass), attributes);
scope.attr(derivedClassName) = thisClass;
}
template <typename Func, typename... Extra>
pure_subclass &def(const char *name, Func &&f, const Extra &... extra) {
py::cpp_function cf(
std::forward<Func>(f), py::name(name), py::is_method(py::none()),
py::sibling(py::getattr(thisClass, name, py::none())), extra...);
thisClass.attr(cf.name()) = cf;
return *this;
}
template <typename Func, typename... Extra>
pure_subclass &def_property_readonly(const char *name, Func &&f,
const Extra &... extra) {
py::cpp_function cf(
std::forward<Func>(f), py::name(name), py::is_method(py::none()),
py::sibling(py::getattr(thisClass, name, py::none())), extra...);
auto builtinProperty =
py::reinterpret_borrow<py::object>((PyObject *)&PyProperty_Type);
thisClass.attr(name) = builtinProperty(cf);
return *this;
}
template <typename Func, typename... Extra>
pure_subclass &def_static(const char *name, Func &&f,
const Extra &... extra) {
static_assert(
!std::is_member_function_pointer<Func>::value,
"def_static(...) called with a non-static member function pointer");
py::cpp_function cf(
std::forward<Func>(f), py::name(name), py::scope(thisClass),
py::sibling(py::getattr(thisClass, name, py::none())), extra...);
thisClass.attr(cf.name()) = py::staticmethod(cf);
return *this;
}
protected:
py::object superClass;
py::object thisClass;
};
class mlir_type_subclass : public pure_subclass {
public:
using IsAFunctionTy = bool (*)(MlirType);
mlir_type_subclass(py::handle scope, const char *typeClassName,
IsAFunctionTy isaFunction)
: mlir_type_subclass(scope, typeClassName, isaFunction, {"Type"}) {}
mlir_type_subclass(py::handle scope, const char *typeClassName,
IsAFunctionTy isaFunction, super_class_info superClassInfo)
: pure_subclass(scope, typeClassName, superClassInfo) {
// Casting constructor. Note that defining an __init__ method is special
// and not yet generalized on pure_subclass (it requires a somewhat
// different cpp_function and other requirements on chaining to super
// __init__ make it more awkward to do generally).
std::string captureTypeName(
typeClassName); // As string in case if typeClassName is not static.
py::object captureSuperClass = superClass;
py::cpp_function initCf(
[captureSuperClass, isaFunction,
captureTypeName](py::object self, py::object otherType) {
MlirType rawType = py::cast<MlirType>(otherType);
if (!isaFunction(rawType)) {
auto origRepr = py::repr(otherType).cast<std::string>();
throw std::invalid_argument((llvm::Twine("Cannot cast type to ") +
captureTypeName + " (from " +
origRepr + ")")
.str());
}
captureSuperClass.attr("__init__")(self, otherType);
},
py::arg("cast_from_type"), py::is_method(py::none()),
"Casts the passed type to this specific sub-type.");
thisClass.attr("__init__") = initCf;
// 'isinstance' method.
def_static(
"isinstance",
[isaFunction](MlirType other) { return isaFunction(other); },
py::arg("other_type"));
}
};
} // namespace adaptors
} // namespace python
} // namespace mlir
#endif // CIRCT_BINDINGS_PYTHON_PYBINDUTILS_H