1 //===- IRTypes.cpp - Exports builtin and standard types -------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "IRModule.h"
10 
11 #include "PybindUtils.h"
12 
13 #include "mlir-c/BuiltinAttributes.h"
14 #include "mlir-c/BuiltinTypes.h"
15 
16 namespace py = pybind11;
17 using namespace mlir;
18 using namespace mlir::python;
19 
20 using llvm::SmallVector;
21 using llvm::Twine;
22 
23 namespace {
24 
25 /// Checks whether the given type is an integer or float type.
mlirTypeIsAIntegerOrFloat(MlirType type)26 static int mlirTypeIsAIntegerOrFloat(MlirType type) {
27   return mlirTypeIsAInteger(type) || mlirTypeIsABF16(type) ||
28          mlirTypeIsAF16(type) || mlirTypeIsAF32(type) || mlirTypeIsAF64(type);
29 }
30 
31 class PyIntegerType : public PyConcreteType<PyIntegerType> {
32 public:
33   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAInteger;
34   static constexpr const char *pyClassName = "IntegerType";
35   using PyConcreteType::PyConcreteType;
36 
bindDerived(ClassTy & c)37   static void bindDerived(ClassTy &c) {
38     c.def_static(
39         "get_signless",
40         [](unsigned width, DefaultingPyMlirContext context) {
41           MlirType t = mlirIntegerTypeGet(context->get(), width);
42           return PyIntegerType(context->getRef(), t);
43         },
44         py::arg("width"), py::arg("context") = py::none(),
45         "Create a signless integer type");
46     c.def_static(
47         "get_signed",
48         [](unsigned width, DefaultingPyMlirContext context) {
49           MlirType t = mlirIntegerTypeSignedGet(context->get(), width);
50           return PyIntegerType(context->getRef(), t);
51         },
52         py::arg("width"), py::arg("context") = py::none(),
53         "Create a signed integer type");
54     c.def_static(
55         "get_unsigned",
56         [](unsigned width, DefaultingPyMlirContext context) {
57           MlirType t = mlirIntegerTypeUnsignedGet(context->get(), width);
58           return PyIntegerType(context->getRef(), t);
59         },
60         py::arg("width"), py::arg("context") = py::none(),
61         "Create an unsigned integer type");
62     c.def_property_readonly(
63         "width",
64         [](PyIntegerType &self) { return mlirIntegerTypeGetWidth(self); },
65         "Returns the width of the integer type");
66     c.def_property_readonly(
67         "is_signless",
68         [](PyIntegerType &self) -> bool {
69           return mlirIntegerTypeIsSignless(self);
70         },
71         "Returns whether this is a signless integer");
72     c.def_property_readonly(
73         "is_signed",
74         [](PyIntegerType &self) -> bool {
75           return mlirIntegerTypeIsSigned(self);
76         },
77         "Returns whether this is a signed integer");
78     c.def_property_readonly(
79         "is_unsigned",
80         [](PyIntegerType &self) -> bool {
81           return mlirIntegerTypeIsUnsigned(self);
82         },
83         "Returns whether this is an unsigned integer");
84   }
85 };
86 
87 /// Index Type subclass - IndexType.
88 class PyIndexType : public PyConcreteType<PyIndexType> {
89 public:
90   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAIndex;
91   static constexpr const char *pyClassName = "IndexType";
92   using PyConcreteType::PyConcreteType;
93 
bindDerived(ClassTy & c)94   static void bindDerived(ClassTy &c) {
95     c.def_static(
96         "get",
97         [](DefaultingPyMlirContext context) {
98           MlirType t = mlirIndexTypeGet(context->get());
99           return PyIndexType(context->getRef(), t);
100         },
101         py::arg("context") = py::none(), "Create a index type.");
102   }
103 };
104 
105 /// Floating Point Type subclass - BF16Type.
106 class PyBF16Type : public PyConcreteType<PyBF16Type> {
107 public:
108   static constexpr IsAFunctionTy isaFunction = mlirTypeIsABF16;
109   static constexpr const char *pyClassName = "BF16Type";
110   using PyConcreteType::PyConcreteType;
111 
bindDerived(ClassTy & c)112   static void bindDerived(ClassTy &c) {
113     c.def_static(
114         "get",
115         [](DefaultingPyMlirContext context) {
116           MlirType t = mlirBF16TypeGet(context->get());
117           return PyBF16Type(context->getRef(), t);
118         },
119         py::arg("context") = py::none(), "Create a bf16 type.");
120   }
121 };
122 
123 /// Floating Point Type subclass - F16Type.
124 class PyF16Type : public PyConcreteType<PyF16Type> {
125 public:
126   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAF16;
127   static constexpr const char *pyClassName = "F16Type";
128   using PyConcreteType::PyConcreteType;
129 
bindDerived(ClassTy & c)130   static void bindDerived(ClassTy &c) {
131     c.def_static(
132         "get",
133         [](DefaultingPyMlirContext context) {
134           MlirType t = mlirF16TypeGet(context->get());
135           return PyF16Type(context->getRef(), t);
136         },
137         py::arg("context") = py::none(), "Create a f16 type.");
138   }
139 };
140 
141 /// Floating Point Type subclass - F32Type.
142 class PyF32Type : public PyConcreteType<PyF32Type> {
143 public:
144   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAF32;
145   static constexpr const char *pyClassName = "F32Type";
146   using PyConcreteType::PyConcreteType;
147 
bindDerived(ClassTy & c)148   static void bindDerived(ClassTy &c) {
149     c.def_static(
150         "get",
151         [](DefaultingPyMlirContext context) {
152           MlirType t = mlirF32TypeGet(context->get());
153           return PyF32Type(context->getRef(), t);
154         },
155         py::arg("context") = py::none(), "Create a f32 type.");
156   }
157 };
158 
159 /// Floating Point Type subclass - F64Type.
160 class PyF64Type : public PyConcreteType<PyF64Type> {
161 public:
162   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAF64;
163   static constexpr const char *pyClassName = "F64Type";
164   using PyConcreteType::PyConcreteType;
165 
bindDerived(ClassTy & c)166   static void bindDerived(ClassTy &c) {
167     c.def_static(
168         "get",
169         [](DefaultingPyMlirContext context) {
170           MlirType t = mlirF64TypeGet(context->get());
171           return PyF64Type(context->getRef(), t);
172         },
173         py::arg("context") = py::none(), "Create a f64 type.");
174   }
175 };
176 
177 /// None Type subclass - NoneType.
178 class PyNoneType : public PyConcreteType<PyNoneType> {
179 public:
180   static constexpr IsAFunctionTy isaFunction = mlirTypeIsANone;
181   static constexpr const char *pyClassName = "NoneType";
182   using PyConcreteType::PyConcreteType;
183 
bindDerived(ClassTy & c)184   static void bindDerived(ClassTy &c) {
185     c.def_static(
186         "get",
187         [](DefaultingPyMlirContext context) {
188           MlirType t = mlirNoneTypeGet(context->get());
189           return PyNoneType(context->getRef(), t);
190         },
191         py::arg("context") = py::none(), "Create a none type.");
192   }
193 };
194 
195 /// Complex Type subclass - ComplexType.
196 class PyComplexType : public PyConcreteType<PyComplexType> {
197 public:
198   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAComplex;
199   static constexpr const char *pyClassName = "ComplexType";
200   using PyConcreteType::PyConcreteType;
201 
bindDerived(ClassTy & c)202   static void bindDerived(ClassTy &c) {
203     c.def_static(
204         "get",
205         [](PyType &elementType) {
206           // The element must be a floating point or integer scalar type.
207           if (mlirTypeIsAIntegerOrFloat(elementType)) {
208             MlirType t = mlirComplexTypeGet(elementType);
209             return PyComplexType(elementType.getContext(), t);
210           }
211           throw SetPyError(
212               PyExc_ValueError,
213               Twine("invalid '") +
214                   py::repr(py::cast(elementType)).cast<std::string>() +
215                   "' and expected floating point or integer type.");
216         },
217         "Create a complex type");
218     c.def_property_readonly(
219         "element_type",
220         [](PyComplexType &self) -> PyType {
221           MlirType t = mlirComplexTypeGetElementType(self);
222           return PyType(self.getContext(), t);
223         },
224         "Returns element type.");
225   }
226 };
227 
228 class PyShapedType : public PyConcreteType<PyShapedType> {
229 public:
230   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAShaped;
231   static constexpr const char *pyClassName = "ShapedType";
232   using PyConcreteType::PyConcreteType;
233 
bindDerived(ClassTy & c)234   static void bindDerived(ClassTy &c) {
235     c.def_property_readonly(
236         "element_type",
237         [](PyShapedType &self) {
238           MlirType t = mlirShapedTypeGetElementType(self);
239           return PyType(self.getContext(), t);
240         },
241         "Returns the element type of the shaped type.");
242     c.def_property_readonly(
243         "has_rank",
244         [](PyShapedType &self) -> bool { return mlirShapedTypeHasRank(self); },
245         "Returns whether the given shaped type is ranked.");
246     c.def_property_readonly(
247         "rank",
248         [](PyShapedType &self) {
249           self.requireHasRank();
250           return mlirShapedTypeGetRank(self);
251         },
252         "Returns the rank of the given ranked shaped type.");
253     c.def_property_readonly(
254         "has_static_shape",
255         [](PyShapedType &self) -> bool {
256           return mlirShapedTypeHasStaticShape(self);
257         },
258         "Returns whether the given shaped type has a static shape.");
259     c.def(
260         "is_dynamic_dim",
261         [](PyShapedType &self, intptr_t dim) -> bool {
262           self.requireHasRank();
263           return mlirShapedTypeIsDynamicDim(self, dim);
264         },
265         "Returns whether the dim-th dimension of the given shaped type is "
266         "dynamic.");
267     c.def(
268         "get_dim_size",
269         [](PyShapedType &self, intptr_t dim) {
270           self.requireHasRank();
271           return mlirShapedTypeGetDimSize(self, dim);
272         },
273         "Returns the dim-th dimension of the given ranked shaped type.");
274     c.def_static(
275         "is_dynamic_size",
276         [](int64_t size) -> bool { return mlirShapedTypeIsDynamicSize(size); },
277         "Returns whether the given dimension size indicates a dynamic "
278         "dimension.");
279     c.def(
280         "is_dynamic_stride_or_offset",
281         [](PyShapedType &self, int64_t val) -> bool {
282           self.requireHasRank();
283           return mlirShapedTypeIsDynamicStrideOrOffset(val);
284         },
285         "Returns whether the given value is used as a placeholder for dynamic "
286         "strides and offsets in shaped types.");
287   }
288 
289 private:
requireHasRank()290   void requireHasRank() {
291     if (!mlirShapedTypeHasRank(*this)) {
292       throw SetPyError(
293           PyExc_ValueError,
294           "calling this method requires that the type has a rank.");
295     }
296   }
297 };
298 
299 /// Vector Type subclass - VectorType.
300 class PyVectorType : public PyConcreteType<PyVectorType, PyShapedType> {
301 public:
302   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAVector;
303   static constexpr const char *pyClassName = "VectorType";
304   using PyConcreteType::PyConcreteType;
305 
bindDerived(ClassTy & c)306   static void bindDerived(ClassTy &c) {
307     c.def_static(
308         "get",
309         [](std::vector<int64_t> shape, PyType &elementType,
310            DefaultingPyLocation loc) {
311           MlirType t = mlirVectorTypeGetChecked(loc, shape.size(), shape.data(),
312                                                 elementType);
313           // TODO: Rework error reporting once diagnostic engine is exposed
314           // in C API.
315           if (mlirTypeIsNull(t)) {
316             throw SetPyError(
317                 PyExc_ValueError,
318                 Twine("invalid '") +
319                     py::repr(py::cast(elementType)).cast<std::string>() +
320                     "' and expected floating point or integer type.");
321           }
322           return PyVectorType(elementType.getContext(), t);
323         },
324         py::arg("shape"), py::arg("elementType"), py::arg("loc") = py::none(),
325         "Create a vector type");
326   }
327 };
328 
329 /// Ranked Tensor Type subclass - RankedTensorType.
330 class PyRankedTensorType
331     : public PyConcreteType<PyRankedTensorType, PyShapedType> {
332 public:
333   static constexpr IsAFunctionTy isaFunction = mlirTypeIsARankedTensor;
334   static constexpr const char *pyClassName = "RankedTensorType";
335   using PyConcreteType::PyConcreteType;
336 
bindDerived(ClassTy & c)337   static void bindDerived(ClassTy &c) {
338     c.def_static(
339         "get",
340         [](std::vector<int64_t> shape, PyType &elementType,
341            llvm::Optional<PyAttribute> &encodingAttr,
342            DefaultingPyLocation loc) {
343           MlirType t = mlirRankedTensorTypeGetChecked(
344               loc, shape.size(), shape.data(), elementType,
345               encodingAttr ? encodingAttr->get() : mlirAttributeGetNull());
346           // TODO: Rework error reporting once diagnostic engine is exposed
347           // in C API.
348           if (mlirTypeIsNull(t)) {
349             throw SetPyError(
350                 PyExc_ValueError,
351                 Twine("invalid '") +
352                     py::repr(py::cast(elementType)).cast<std::string>() +
353                     "' and expected floating point, integer, vector or "
354                     "complex "
355                     "type.");
356           }
357           return PyRankedTensorType(elementType.getContext(), t);
358         },
359         py::arg("shape"), py::arg("element_type"),
360         py::arg("encoding") = py::none(), py::arg("loc") = py::none(),
361         "Create a ranked tensor type");
362     c.def_property_readonly(
363         "encoding",
364         [](PyRankedTensorType &self) -> llvm::Optional<PyAttribute> {
365           MlirAttribute encoding = mlirRankedTensorTypeGetEncoding(self.get());
366           if (mlirAttributeIsNull(encoding))
367             return llvm::None;
368           return PyAttribute(self.getContext(), encoding);
369         });
370   }
371 };
372 
373 /// Unranked Tensor Type subclass - UnrankedTensorType.
374 class PyUnrankedTensorType
375     : public PyConcreteType<PyUnrankedTensorType, PyShapedType> {
376 public:
377   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAUnrankedTensor;
378   static constexpr const char *pyClassName = "UnrankedTensorType";
379   using PyConcreteType::PyConcreteType;
380 
bindDerived(ClassTy & c)381   static void bindDerived(ClassTy &c) {
382     c.def_static(
383         "get",
384         [](PyType &elementType, DefaultingPyLocation loc) {
385           MlirType t = mlirUnrankedTensorTypeGetChecked(loc, elementType);
386           // TODO: Rework error reporting once diagnostic engine is exposed
387           // in C API.
388           if (mlirTypeIsNull(t)) {
389             throw SetPyError(
390                 PyExc_ValueError,
391                 Twine("invalid '") +
392                     py::repr(py::cast(elementType)).cast<std::string>() +
393                     "' and expected floating point, integer, vector or "
394                     "complex "
395                     "type.");
396           }
397           return PyUnrankedTensorType(elementType.getContext(), t);
398         },
399         py::arg("element_type"), py::arg("loc") = py::none(),
400         "Create a unranked tensor type");
401   }
402 };
403 
404 class PyMemRefLayoutMapList;
405 
406 /// Ranked MemRef Type subclass - MemRefType.
407 class PyMemRefType : public PyConcreteType<PyMemRefType, PyShapedType> {
408 public:
409   static constexpr IsAFunctionTy isaFunction = mlirTypeIsARankedTensor;
410   static constexpr const char *pyClassName = "MemRefType";
411   using PyConcreteType::PyConcreteType;
412 
413   PyMemRefLayoutMapList getLayout();
414 
bindDerived(ClassTy & c)415   static void bindDerived(ClassTy &c) {
416     c.def_static(
417          "get",
418          [](std::vector<int64_t> shape, PyType &elementType,
419             std::vector<PyAffineMap> layout, PyAttribute *memorySpace,
420             DefaultingPyLocation loc) {
421            SmallVector<MlirAffineMap> maps;
422            maps.reserve(layout.size());
423            for (PyAffineMap &map : layout)
424              maps.push_back(map);
425 
426            MlirAttribute memSpaceAttr = {};
427            if (memorySpace)
428              memSpaceAttr = *memorySpace;
429 
430            MlirType t = mlirMemRefTypeGetChecked(loc, elementType, shape.size(),
431                                                  shape.data(), maps.size(),
432                                                  maps.data(), memSpaceAttr);
433            // TODO: Rework error reporting once diagnostic engine is exposed
434            // in C API.
435            if (mlirTypeIsNull(t)) {
436              throw SetPyError(
437                  PyExc_ValueError,
438                  Twine("invalid '") +
439                      py::repr(py::cast(elementType)).cast<std::string>() +
440                      "' and expected floating point, integer, vector or "
441                      "complex "
442                      "type.");
443            }
444            return PyMemRefType(elementType.getContext(), t);
445          },
446          py::arg("shape"), py::arg("element_type"),
447          py::arg("layout") = py::list(), py::arg("memory_space") = py::none(),
448          py::arg("loc") = py::none(), "Create a memref type")
449         .def_property_readonly("layout", &PyMemRefType::getLayout,
450                                "The list of layout maps of the MemRef type.")
451         .def_property_readonly(
452             "memory_space",
453             [](PyMemRefType &self) -> PyAttribute {
454               MlirAttribute a = mlirMemRefTypeGetMemorySpace(self);
455               return PyAttribute(self.getContext(), a);
456             },
457             "Returns the memory space of the given MemRef type.");
458   }
459 };
460 
461 /// A list of affine layout maps in a memref type. Internally, these are stored
462 /// as consecutive elements, random access is cheap. Both the type and the maps
463 /// are owned by the context, no need to worry about lifetime extension.
464 class PyMemRefLayoutMapList
465     : public Sliceable<PyMemRefLayoutMapList, PyAffineMap> {
466 public:
467   static constexpr const char *pyClassName = "MemRefLayoutMapList";
468 
PyMemRefLayoutMapList(PyMemRefType type,intptr_t startIndex=0,intptr_t length=-1,intptr_t step=1)469   PyMemRefLayoutMapList(PyMemRefType type, intptr_t startIndex = 0,
470                         intptr_t length = -1, intptr_t step = 1)
471       : Sliceable(startIndex,
472                   length == -1 ? mlirMemRefTypeGetNumAffineMaps(type) : length,
473                   step),
474         memref(type) {}
475 
getNumElements()476   intptr_t getNumElements() { return mlirMemRefTypeGetNumAffineMaps(memref); }
477 
getElement(intptr_t index)478   PyAffineMap getElement(intptr_t index) {
479     return PyAffineMap(memref.getContext(),
480                        mlirMemRefTypeGetAffineMap(memref, index));
481   }
482 
slice(intptr_t startIndex,intptr_t length,intptr_t step)483   PyMemRefLayoutMapList slice(intptr_t startIndex, intptr_t length,
484                               intptr_t step) {
485     return PyMemRefLayoutMapList(memref, startIndex, length, step);
486   }
487 
488 private:
489   PyMemRefType memref;
490 };
491 
getLayout()492 PyMemRefLayoutMapList PyMemRefType::getLayout() {
493   return PyMemRefLayoutMapList(*this);
494 }
495 
496 /// Unranked MemRef Type subclass - UnrankedMemRefType.
497 class PyUnrankedMemRefType
498     : public PyConcreteType<PyUnrankedMemRefType, PyShapedType> {
499 public:
500   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAUnrankedMemRef;
501   static constexpr const char *pyClassName = "UnrankedMemRefType";
502   using PyConcreteType::PyConcreteType;
503 
bindDerived(ClassTy & c)504   static void bindDerived(ClassTy &c) {
505     c.def_static(
506          "get",
507          [](PyType &elementType, PyAttribute *memorySpace,
508             DefaultingPyLocation loc) {
509            MlirAttribute memSpaceAttr = {};
510            if (memorySpace)
511              memSpaceAttr = *memorySpace;
512 
513            MlirType t =
514                mlirUnrankedMemRefTypeGetChecked(loc, elementType, memSpaceAttr);
515            // TODO: Rework error reporting once diagnostic engine is exposed
516            // in C API.
517            if (mlirTypeIsNull(t)) {
518              throw SetPyError(
519                  PyExc_ValueError,
520                  Twine("invalid '") +
521                      py::repr(py::cast(elementType)).cast<std::string>() +
522                      "' and expected floating point, integer, vector or "
523                      "complex "
524                      "type.");
525            }
526            return PyUnrankedMemRefType(elementType.getContext(), t);
527          },
528          py::arg("element_type"), py::arg("memory_space"),
529          py::arg("loc") = py::none(), "Create a unranked memref type")
530         .def_property_readonly(
531             "memory_space",
532             [](PyUnrankedMemRefType &self) -> PyAttribute {
533               MlirAttribute a = mlirMemRefTypeGetMemorySpace(self);
534               return PyAttribute(self.getContext(), a);
535             },
536             "Returns the memory space of the given Unranked MemRef type.");
537   }
538 };
539 
540 /// Tuple Type subclass - TupleType.
541 class PyTupleType : public PyConcreteType<PyTupleType> {
542 public:
543   static constexpr IsAFunctionTy isaFunction = mlirTypeIsATuple;
544   static constexpr const char *pyClassName = "TupleType";
545   using PyConcreteType::PyConcreteType;
546 
bindDerived(ClassTy & c)547   static void bindDerived(ClassTy &c) {
548     c.def_static(
549         "get_tuple",
550         [](py::list elementList, DefaultingPyMlirContext context) {
551           intptr_t num = py::len(elementList);
552           // Mapping py::list to SmallVector.
553           SmallVector<MlirType, 4> elements;
554           for (auto element : elementList)
555             elements.push_back(element.cast<PyType>());
556           MlirType t = mlirTupleTypeGet(context->get(), num, elements.data());
557           return PyTupleType(context->getRef(), t);
558         },
559         py::arg("elements"), py::arg("context") = py::none(),
560         "Create a tuple type");
561     c.def(
562         "get_type",
563         [](PyTupleType &self, intptr_t pos) -> PyType {
564           MlirType t = mlirTupleTypeGetType(self, pos);
565           return PyType(self.getContext(), t);
566         },
567         "Returns the pos-th type in the tuple type.");
568     c.def_property_readonly(
569         "num_types",
570         [](PyTupleType &self) -> intptr_t {
571           return mlirTupleTypeGetNumTypes(self);
572         },
573         "Returns the number of types contained in a tuple.");
574   }
575 };
576 
577 /// Function type.
578 class PyFunctionType : public PyConcreteType<PyFunctionType> {
579 public:
580   static constexpr IsAFunctionTy isaFunction = mlirTypeIsAFunction;
581   static constexpr const char *pyClassName = "FunctionType";
582   using PyConcreteType::PyConcreteType;
583 
bindDerived(ClassTy & c)584   static void bindDerived(ClassTy &c) {
585     c.def_static(
586         "get",
587         [](std::vector<PyType> inputs, std::vector<PyType> results,
588            DefaultingPyMlirContext context) {
589           SmallVector<MlirType, 4> inputsRaw(inputs.begin(), inputs.end());
590           SmallVector<MlirType, 4> resultsRaw(results.begin(), results.end());
591           MlirType t = mlirFunctionTypeGet(context->get(), inputsRaw.size(),
592                                            inputsRaw.data(), resultsRaw.size(),
593                                            resultsRaw.data());
594           return PyFunctionType(context->getRef(), t);
595         },
596         py::arg("inputs"), py::arg("results"), py::arg("context") = py::none(),
597         "Gets a FunctionType from a list of input and result types");
598     c.def_property_readonly(
599         "inputs",
600         [](PyFunctionType &self) {
601           MlirType t = self;
602           auto contextRef = self.getContext();
603           py::list types;
604           for (intptr_t i = 0, e = mlirFunctionTypeGetNumInputs(self); i < e;
605                ++i) {
606             types.append(PyType(contextRef, mlirFunctionTypeGetInput(t, i)));
607           }
608           return types;
609         },
610         "Returns the list of input types in the FunctionType.");
611     c.def_property_readonly(
612         "results",
613         [](PyFunctionType &self) {
614           auto contextRef = self.getContext();
615           py::list types;
616           for (intptr_t i = 0, e = mlirFunctionTypeGetNumResults(self); i < e;
617                ++i) {
618             types.append(
619                 PyType(contextRef, mlirFunctionTypeGetResult(self, i)));
620           }
621           return types;
622         },
623         "Returns the list of result types in the FunctionType.");
624   }
625 };
626 
627 } // namespace
628 
populateIRTypes(py::module & m)629 void mlir::python::populateIRTypes(py::module &m) {
630   PyIntegerType::bind(m);
631   PyIndexType::bind(m);
632   PyBF16Type::bind(m);
633   PyF16Type::bind(m);
634   PyF32Type::bind(m);
635   PyF64Type::bind(m);
636   PyNoneType::bind(m);
637   PyComplexType::bind(m);
638   PyShapedType::bind(m);
639   PyVectorType::bind(m);
640   PyRankedTensorType::bind(m);
641   PyUnrankedTensorType::bind(m);
642   PyMemRefType::bind(m);
643   PyMemRefLayoutMapList::bind(m);
644   PyUnrankedMemRefType::bind(m);
645   PyTupleType::bind(m);
646   PyFunctionType::bind(m);
647 }
648