xref: /llvm-project/mlir/tools/mlir-tblgen/OpPythonBindingGen.cpp (revision acde3f722ff3766f6f793884108d342b78623fe4)
1fd407e1fSAlex Zinenko //===- OpPythonBindingGen.cpp - Generator of Python API for MLIR Ops ------===//
2fd407e1fSAlex Zinenko //
3fd407e1fSAlex Zinenko // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fd407e1fSAlex Zinenko // See https://llvm.org/LICENSE.txt for license information.
5fd407e1fSAlex Zinenko // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fd407e1fSAlex Zinenko //
7fd407e1fSAlex Zinenko //===----------------------------------------------------------------------===//
8fd407e1fSAlex Zinenko //
9fd407e1fSAlex Zinenko // OpPythonBindingGen uses ODS specification of MLIR ops to generate Python
10fd407e1fSAlex Zinenko // binding classes wrapping a generic operation API.
11fd407e1fSAlex Zinenko //
12fd407e1fSAlex Zinenko //===----------------------------------------------------------------------===//
13fd407e1fSAlex Zinenko 
1492233062Smax #include "OpGenHelpers.h"
1592233062Smax 
16fd407e1fSAlex Zinenko #include "mlir/TableGen/GenInfo.h"
17fd407e1fSAlex Zinenko #include "mlir/TableGen/Operator.h"
18fd407e1fSAlex Zinenko #include "llvm/ADT/StringSet.h"
19fd407e1fSAlex Zinenko #include "llvm/Support/CommandLine.h"
20fd407e1fSAlex Zinenko #include "llvm/Support/FormatVariadic.h"
21fd407e1fSAlex Zinenko #include "llvm/TableGen/Error.h"
22fd407e1fSAlex Zinenko #include "llvm/TableGen/Record.h"
23fd407e1fSAlex Zinenko 
24fd407e1fSAlex Zinenko using namespace mlir;
25fd407e1fSAlex Zinenko using namespace mlir::tblgen;
26bccd37f6SRahul Joshi using llvm::formatv;
27bccd37f6SRahul Joshi using llvm::Record;
28bccd37f6SRahul Joshi using llvm::RecordKeeper;
29fd407e1fSAlex Zinenko 
30fd407e1fSAlex Zinenko /// File header and includes.
31894d88a7SStella Laurenzo ///   {0} is the dialect namespace.
32fd407e1fSAlex Zinenko constexpr const char *fileHeader = R"Py(
33fd407e1fSAlex Zinenko # Autogenerated by mlir-tblgen; don't manually edit.
34fd407e1fSAlex Zinenko 
35e31c77b1SStella Laurenzo from ._ods_common import _cext as _ods_cext
367c850867SMaksim Levental from ._ods_common import (
377c850867SMaksim Levental     equally_sized_accessor as _ods_equally_sized_accessor,
387c850867SMaksim Levental     get_default_loc_context as _ods_get_default_loc_context,
397c850867SMaksim Levental     get_op_result_or_op_results as _get_op_result_or_op_results,
407c850867SMaksim Levental     get_op_results_or_values as _get_op_results_or_values,
417c850867SMaksim Levental     segmented_accessor as _ods_segmented_accessor,
427c850867SMaksim Levental )
438a1f1a10SStella Laurenzo _ods_ir = _ods_cext.ir
44894d88a7SStella Laurenzo 
45b4c93eceSJohn Demme import builtins
4627c6d55cSMaksim Levental from typing import Sequence as _Sequence, Union as _Union
47b4c93eceSJohn Demme 
48fd407e1fSAlex Zinenko )Py";
49fd407e1fSAlex Zinenko 
50fd407e1fSAlex Zinenko /// Template for dialect class:
51fd407e1fSAlex Zinenko ///   {0} is the dialect namespace.
52fd407e1fSAlex Zinenko constexpr const char *dialectClassTemplate = R"Py(
538a1f1a10SStella Laurenzo @_ods_cext.register_dialect
548a1f1a10SStella Laurenzo class _Dialect(_ods_ir.Dialect):
55fd407e1fSAlex Zinenko   DIALECT_NAMESPACE = "{0}"
56fd407e1fSAlex Zinenko )Py";
57fd407e1fSAlex Zinenko 
583f71765aSAlex Zinenko constexpr const char *dialectExtensionTemplate = R"Py(
593f71765aSAlex Zinenko from ._{0}_ops_gen import _Dialect
603f71765aSAlex Zinenko )Py";
613f71765aSAlex Zinenko 
62fd407e1fSAlex Zinenko /// Template for operation class:
63fd407e1fSAlex Zinenko ///   {0} is the Python class name;
64fd407e1fSAlex Zinenko ///   {1} is the operation name.
65fd407e1fSAlex Zinenko constexpr const char *opClassTemplate = R"Py(
668a1f1a10SStella Laurenzo @_ods_cext.register_operation(_Dialect)
678a1f1a10SStella Laurenzo class {0}(_ods_ir.OpView):
68fd407e1fSAlex Zinenko   OPERATION_NAME = "{1}"
69fd407e1fSAlex Zinenko )Py";
70fd407e1fSAlex Zinenko 
7171b6b010SStella Laurenzo /// Template for class level declarations of operand and result
7271b6b010SStella Laurenzo /// segment specs.
7371b6b010SStella Laurenzo ///   {0} is either "OPERAND" or "RESULT"
7471b6b010SStella Laurenzo ///   {1} is the segment spec
7571b6b010SStella Laurenzo /// Each segment spec is either None (default) or an array of integers
7671b6b010SStella Laurenzo /// where:
7771b6b010SStella Laurenzo ///   1 = single element (expect non sequence operand/result)
78192d9dd7SKazu Hirata ///   0 = optional element (expect a value or std::nullopt)
7971b6b010SStella Laurenzo ///   -1 = operand/result is a sequence corresponding to a variadic
8071b6b010SStella Laurenzo constexpr const char *opClassSizedSegmentsTemplate = R"Py(
8171b6b010SStella Laurenzo   _ODS_{0}_SEGMENTS = {1}
8271b6b010SStella Laurenzo )Py";
8371b6b010SStella Laurenzo 
8471b6b010SStella Laurenzo /// Template for class level declarations of the _ODS_REGIONS spec:
8571b6b010SStella Laurenzo ///   {0} is the minimum number of regions
8671b6b010SStella Laurenzo ///   {1} is the Python bool literal for hasNoVariadicRegions
8771b6b010SStella Laurenzo constexpr const char *opClassRegionSpecTemplate = R"Py(
8871b6b010SStella Laurenzo   _ODS_REGIONS = ({0}, {1})
8971b6b010SStella Laurenzo )Py";
9071b6b010SStella Laurenzo 
91fd407e1fSAlex Zinenko /// Template for single-element accessor:
92fd407e1fSAlex Zinenko ///   {0} is the name of the accessor;
93fd407e1fSAlex Zinenko ///   {1} is either 'operand' or 'result';
94fd407e1fSAlex Zinenko ///   {2} is the position in the element list.
95fd407e1fSAlex Zinenko constexpr const char *opSingleTemplate = R"Py(
96b4c93eceSJohn Demme   @builtins.property
97fd407e1fSAlex Zinenko   def {0}(self):
98fd407e1fSAlex Zinenko     return self.operation.{1}s[{2}]
99fd407e1fSAlex Zinenko )Py";
100fd407e1fSAlex Zinenko 
101fd407e1fSAlex Zinenko /// Template for single-element accessor after a variable-length group:
102fd407e1fSAlex Zinenko ///   {0} is the name of the accessor;
103fd407e1fSAlex Zinenko ///   {1} is either 'operand' or 'result';
104fd407e1fSAlex Zinenko ///   {2} is the total number of element groups;
105fd407e1fSAlex Zinenko ///   {3} is the position of the current group in the group list.
106fd407e1fSAlex Zinenko /// This works for both a single variadic group (non-negative length) and an
107fd407e1fSAlex Zinenko /// single optional element (zero length if the element is absent).
108fd407e1fSAlex Zinenko constexpr const char *opSingleAfterVariableTemplate = R"Py(
109b4c93eceSJohn Demme   @builtins.property
110fd407e1fSAlex Zinenko   def {0}(self):
1118a1f1a10SStella Laurenzo     _ods_variadic_group_length = len(self.operation.{1}s) - {2} + 1
1128a1f1a10SStella Laurenzo     return self.operation.{1}s[{3} + _ods_variadic_group_length - 1]
113fd407e1fSAlex Zinenko )Py";
114fd407e1fSAlex Zinenko 
115fd407e1fSAlex Zinenko /// Template for an optional element accessor:
116fd407e1fSAlex Zinenko ///   {0} is the name of the accessor;
117fd407e1fSAlex Zinenko ///   {1} is either 'operand' or 'result';
118fd407e1fSAlex Zinenko ///   {2} is the total number of element groups;
119fd407e1fSAlex Zinenko ///   {3} is the position of the current group in the group list.
12054c99842SMichal Terepeta /// This works if we have only one variable-length group (and it's the optional
12154c99842SMichal Terepeta /// operand/result): we can deduce it's absent if the `len(operation.{1}s)` is
12254c99842SMichal Terepeta /// smaller than the total number of groups.
123fd407e1fSAlex Zinenko constexpr const char *opOneOptionalTemplate = R"Py(
124b4c93eceSJohn Demme   @builtins.property
125fd226c9bSStella Laurenzo   def {0}(self):
12654c99842SMichal Terepeta     return None if len(self.operation.{1}s) < {2} else self.operation.{1}s[{3}]
127fd407e1fSAlex Zinenko )Py";
128fd407e1fSAlex Zinenko 
129fd407e1fSAlex Zinenko /// Template for the variadic group accessor in the single variadic group case:
130fd407e1fSAlex Zinenko ///   {0} is the name of the accessor;
131fd407e1fSAlex Zinenko ///   {1} is either 'operand' or 'result';
132fd407e1fSAlex Zinenko ///   {2} is the total number of element groups;
133fd407e1fSAlex Zinenko ///   {3} is the position of the current group in the group list.
134fd407e1fSAlex Zinenko constexpr const char *opOneVariadicTemplate = R"Py(
135b4c93eceSJohn Demme   @builtins.property
136fd407e1fSAlex Zinenko   def {0}(self):
1378a1f1a10SStella Laurenzo     _ods_variadic_group_length = len(self.operation.{1}s) - {2} + 1
1388a1f1a10SStella Laurenzo     return self.operation.{1}s[{3}:{3} + _ods_variadic_group_length]
139fd407e1fSAlex Zinenko )Py";
140fd407e1fSAlex Zinenko 
141fd407e1fSAlex Zinenko /// First part of the template for equally-sized variadic group accessor:
142fd407e1fSAlex Zinenko ///   {0} is the name of the accessor;
143fd407e1fSAlex Zinenko ///   {1} is either 'operand' or 'result';
1443766ba44SKasper Nielsen ///   {2} is the total number of non-variadic groups;
1453766ba44SKasper Nielsen ///   {3} is the total number of variadic groups;
1463766ba44SKasper Nielsen ///   {4} is the number of non-variadic groups preceding the current group;
1473766ba44SKasper Nielsen ///   {5} is the number of variadic groups preceding the current group.
148fd407e1fSAlex Zinenko constexpr const char *opVariadicEqualPrefixTemplate = R"Py(
149b4c93eceSJohn Demme   @builtins.property
150fd407e1fSAlex Zinenko   def {0}(self):
1513766ba44SKasper Nielsen     start, elements_per_group = _ods_equally_sized_accessor(self.operation.{1}s, {2}, {3}, {4}, {5}))Py";
152fd407e1fSAlex Zinenko 
153fd407e1fSAlex Zinenko /// Second part of the template for equally-sized case, accessing a single
154fd407e1fSAlex Zinenko /// element:
155fd407e1fSAlex Zinenko ///   {0} is either 'operand' or 'result'.
156fd407e1fSAlex Zinenko constexpr const char *opVariadicEqualSimpleTemplate = R"Py(
157fd407e1fSAlex Zinenko     return self.operation.{0}s[start]
158fd407e1fSAlex Zinenko )Py";
159fd407e1fSAlex Zinenko 
160fd407e1fSAlex Zinenko /// Second part of the template for equally-sized case, accessing a variadic
161fd407e1fSAlex Zinenko /// group:
162fd407e1fSAlex Zinenko ///   {0} is either 'operand' or 'result'.
163fd407e1fSAlex Zinenko constexpr const char *opVariadicEqualVariadicTemplate = R"Py(
1643766ba44SKasper Nielsen     return self.operation.{0}s[start:start + elements_per_group]
165fd407e1fSAlex Zinenko )Py";
166fd407e1fSAlex Zinenko 
167fd407e1fSAlex Zinenko /// Template for an attribute-sized group accessor:
168fd407e1fSAlex Zinenko ///   {0} is the name of the accessor;
169fd407e1fSAlex Zinenko ///   {1} is either 'operand' or 'result';
170fd407e1fSAlex Zinenko ///   {2} is the position of the group in the group list;
171fd407e1fSAlex Zinenko ///   {3} is a return suffix (expected [0] for single-element, empty for
172fd407e1fSAlex Zinenko ///       variadic, and opVariadicSegmentOptionalTrailingTemplate for optional).
173fd407e1fSAlex Zinenko constexpr const char *opVariadicSegmentTemplate = R"Py(
174b4c93eceSJohn Demme   @builtins.property
175fd407e1fSAlex Zinenko   def {0}(self):
1768a1f1a10SStella Laurenzo     {1}_range = _ods_segmented_accessor(
177fd407e1fSAlex Zinenko          self.operation.{1}s,
178363b6559SMehdi Amini          self.operation.attributes["{1}SegmentSizes"], {2})
179fd407e1fSAlex Zinenko     return {1}_range{3}
180fd407e1fSAlex Zinenko )Py";
181fd407e1fSAlex Zinenko 
182fd407e1fSAlex Zinenko /// Template for a suffix when accessing an optional element in the
183fd407e1fSAlex Zinenko /// attribute-sized case:
184fd407e1fSAlex Zinenko ///   {0} is either 'operand' or 'result';
185fd407e1fSAlex Zinenko constexpr const char *opVariadicSegmentOptionalTrailingTemplate =
186fd407e1fSAlex Zinenko     R"Py([0] if len({0}_range) > 0 else None)Py";
187fd407e1fSAlex Zinenko 
188c5a6712fSAlex Zinenko /// Template for an operation attribute getter:
189c5a6712fSAlex Zinenko ///   {0} is the name of the attribute sanitized for Python;
19067a910bbSRahul Kayaith ///   {1} is the original name of the attribute.
191c5a6712fSAlex Zinenko constexpr const char *attributeGetterTemplate = R"Py(
192b4c93eceSJohn Demme   @builtins.property
193c5a6712fSAlex Zinenko   def {0}(self):
19467a910bbSRahul Kayaith     return self.operation.attributes["{1}"]
195c5a6712fSAlex Zinenko )Py";
196c5a6712fSAlex Zinenko 
197c5a6712fSAlex Zinenko /// Template for an optional operation attribute getter:
198c5a6712fSAlex Zinenko ///   {0} is the name of the attribute sanitized for Python;
19967a910bbSRahul Kayaith ///   {1} is the original name of the attribute.
200c5a6712fSAlex Zinenko constexpr const char *optionalAttributeGetterTemplate = R"Py(
201b4c93eceSJohn Demme   @builtins.property
202c5a6712fSAlex Zinenko   def {0}(self):
20367a910bbSRahul Kayaith     if "{1}" not in self.operation.attributes:
204c5a6712fSAlex Zinenko       return None
20567a910bbSRahul Kayaith     return self.operation.attributes["{1}"]
206c5a6712fSAlex Zinenko )Py";
207c5a6712fSAlex Zinenko 
208029e199dSAlex Zinenko /// Template for a getter of a unit operation attribute, returns True of the
209c5a6712fSAlex Zinenko /// unit attribute is present, False otherwise (unit attributes have meaning
210c5a6712fSAlex Zinenko /// by mere presence):
211c5a6712fSAlex Zinenko ///    {0} is the name of the attribute sanitized for Python,
212c5a6712fSAlex Zinenko ///    {1} is the original name of the attribute.
213c5a6712fSAlex Zinenko constexpr const char *unitAttributeGetterTemplate = R"Py(
214b4c93eceSJohn Demme   @builtins.property
215c5a6712fSAlex Zinenko   def {0}(self):
216c5a6712fSAlex Zinenko     return "{1}" in self.operation.attributes
217c5a6712fSAlex Zinenko )Py";
218c5a6712fSAlex Zinenko 
219029e199dSAlex Zinenko /// Template for an operation attribute setter:
220029e199dSAlex Zinenko ///    {0} is the name of the attribute sanitized for Python;
221029e199dSAlex Zinenko ///    {1} is the original name of the attribute.
222029e199dSAlex Zinenko constexpr const char *attributeSetterTemplate = R"Py(
223029e199dSAlex Zinenko   @{0}.setter
224029e199dSAlex Zinenko   def {0}(self, value):
225029e199dSAlex Zinenko     if value is None:
226029e199dSAlex Zinenko       raise ValueError("'None' not allowed as value for mandatory attributes")
227029e199dSAlex Zinenko     self.operation.attributes["{1}"] = value
228029e199dSAlex Zinenko )Py";
229029e199dSAlex Zinenko 
230029e199dSAlex Zinenko /// Template for a setter of an optional operation attribute, setting to None
231029e199dSAlex Zinenko /// removes the attribute:
232029e199dSAlex Zinenko ///    {0} is the name of the attribute sanitized for Python;
233029e199dSAlex Zinenko ///    {1} is the original name of the attribute.
234029e199dSAlex Zinenko constexpr const char *optionalAttributeSetterTemplate = R"Py(
235029e199dSAlex Zinenko   @{0}.setter
236029e199dSAlex Zinenko   def {0}(self, value):
237029e199dSAlex Zinenko     if value is not None:
238029e199dSAlex Zinenko       self.operation.attributes["{1}"] = value
239029e199dSAlex Zinenko     elif "{1}" in self.operation.attributes:
240029e199dSAlex Zinenko       del self.operation.attributes["{1}"]
241029e199dSAlex Zinenko )Py";
242029e199dSAlex Zinenko 
243029e199dSAlex Zinenko /// Template for a setter of a unit operation attribute, setting to None or
244029e199dSAlex Zinenko /// False removes the attribute:
245029e199dSAlex Zinenko ///    {0} is the name of the attribute sanitized for Python;
246029e199dSAlex Zinenko ///    {1} is the original name of the attribute.
247029e199dSAlex Zinenko constexpr const char *unitAttributeSetterTemplate = R"Py(
248029e199dSAlex Zinenko   @{0}.setter
249029e199dSAlex Zinenko   def {0}(self, value):
250029e199dSAlex Zinenko     if bool(value):
2518a1f1a10SStella Laurenzo       self.operation.attributes["{1}"] = _ods_ir.UnitAttr.get()
252029e199dSAlex Zinenko     elif "{1}" in self.operation.attributes:
253029e199dSAlex Zinenko       del self.operation.attributes["{1}"]
254029e199dSAlex Zinenko )Py";
255029e199dSAlex Zinenko 
256029e199dSAlex Zinenko /// Template for a deleter of an optional or a unit operation attribute, removes
257029e199dSAlex Zinenko /// the attribute from the operation:
258029e199dSAlex Zinenko ///    {0} is the name of the attribute sanitized for Python;
259029e199dSAlex Zinenko ///    {1} is the original name of the attribute.
260029e199dSAlex Zinenko constexpr const char *attributeDeleterTemplate = R"Py(
261029e199dSAlex Zinenko   @{0}.deleter
262029e199dSAlex Zinenko   def {0}(self):
263029e199dSAlex Zinenko     del self.operation.attributes["{1}"]
264029e199dSAlex Zinenko )Py";
265029e199dSAlex Zinenko 
26627c6d55cSMaksim Levental constexpr const char *regionAccessorTemplate = R"Py(
26718fbd5feSAlex Zinenko   @builtins.property
268310736e0SAlex Zinenko   def {0}(self):
26918fbd5feSAlex Zinenko     return self.regions[{1}]
27027c6d55cSMaksim Levental )Py";
27127c6d55cSMaksim Levental 
27227c6d55cSMaksim Levental constexpr const char *valueBuilderTemplate = R"Py(
27327c6d55cSMaksim Levental def {0}({2}) -> {4}:
27451a4f319SPeter Hawkins   return {1}({3}){5}
27551a4f319SPeter Hawkins )Py";
27651a4f319SPeter Hawkins 
27751a4f319SPeter Hawkins constexpr const char *valueBuilderVariadicTemplate = R"Py(
27851a4f319SPeter Hawkins def {0}({2}) -> {4}:
27927c6d55cSMaksim Levental   return _get_op_result_or_op_results({1}({3}))
28027c6d55cSMaksim Levental )Py";
28118fbd5feSAlex Zinenko 
282fd407e1fSAlex Zinenko static llvm::cl::OptionCategory
283fd407e1fSAlex Zinenko     clOpPythonBindingCat("Options for -gen-python-op-bindings");
284fd407e1fSAlex Zinenko 
285fd407e1fSAlex Zinenko static llvm::cl::opt<std::string>
286fd407e1fSAlex Zinenko     clDialectName("bind-dialect",
287fd407e1fSAlex Zinenko                   llvm::cl::desc("The dialect to run the generator for"),
288fd407e1fSAlex Zinenko                   llvm::cl::init(""), llvm::cl::cat(clOpPythonBindingCat));
289fd407e1fSAlex Zinenko 
2903f71765aSAlex Zinenko static llvm::cl::opt<std::string> clDialectExtensionName(
2913f71765aSAlex Zinenko     "dialect-extension", llvm::cl::desc("The prefix of the dialect extension"),
2923f71765aSAlex Zinenko     llvm::cl::init(""), llvm::cl::cat(clOpPythonBindingCat));
2933f71765aSAlex Zinenko 
294c5a6712fSAlex Zinenko using AttributeClasses = DenseMap<StringRef, StringRef>;
295c5a6712fSAlex Zinenko 
2968a1f1a10SStella Laurenzo /// Checks whether `str` would shadow a generated variable or attribute
2978a1f1a10SStella Laurenzo /// part of the OpView API.
2988a1f1a10SStella Laurenzo static bool isODSReserved(StringRef str) {
2998a1f1a10SStella Laurenzo   static llvm::StringSet<> reserved(
3008a1f1a10SStella Laurenzo       {"attributes", "create", "context", "ip", "operands", "print", "get_asm",
301fd226c9bSStella Laurenzo        "loc", "verify", "regions", "results", "self", "operation",
3028a1f1a10SStella Laurenzo        "DIALECT_NAMESPACE", "OPERATION_NAME"});
30388d319a2SKazu Hirata   return str.starts_with("_ods_") || str.ends_with("_ods") ||
3048a1f1a10SStella Laurenzo          reserved.contains(str);
3058a1f1a10SStella Laurenzo }
3068a1f1a10SStella Laurenzo 
307fd407e1fSAlex Zinenko /// Modifies the `name` in a way that it becomes suitable for Python bindings
308fd407e1fSAlex Zinenko /// (does not change the `name` if it already is suitable) and returns the
309fd407e1fSAlex Zinenko /// modified version.
310fd407e1fSAlex Zinenko static std::string sanitizeName(StringRef name) {
311a2288a89SMaksim Levental   std::string processedStr = name.str();
31244600baeSJoelWee   std::replace_if(
313a2288a89SMaksim Levental       processedStr.begin(), processedStr.end(),
31444600baeSJoelWee       [](char c) { return !llvm::isAlnum(c); }, '_');
3157d4cd47eSJoelWee 
316a2288a89SMaksim Levental   if (llvm::isDigit(*processedStr.begin()))
317a2288a89SMaksim Levental     return "_" + processedStr;
3187d4cd47eSJoelWee 
319a2288a89SMaksim Levental   if (isPythonReserved(processedStr) || isODSReserved(processedStr))
320a2288a89SMaksim Levental     return processedStr + "_";
321a2288a89SMaksim Levental   return processedStr;
322fd407e1fSAlex Zinenko }
323fd407e1fSAlex Zinenko 
324f9265de8SAlex Zinenko static std::string attrSizedTraitForKind(const char *kind) {
325bccd37f6SRahul Joshi   return formatv("::mlir::OpTrait::AttrSized{0}{1}Segments",
326bccd37f6SRahul Joshi                  StringRef(kind).take_front().upper(),
327bccd37f6SRahul Joshi                  StringRef(kind).drop_front());
328f9265de8SAlex Zinenko }
329f9265de8SAlex Zinenko 
330fd407e1fSAlex Zinenko /// Emits accessors to "elements" of an Op definition. Currently, the supported
331fd407e1fSAlex Zinenko /// elements are operands and results, indicated by `kind`, which must be either
332fd407e1fSAlex Zinenko /// `operand` or `result` and is used verbatim in the emitted code.
333fd407e1fSAlex Zinenko static void emitElementAccessors(
334fd407e1fSAlex Zinenko     const Operator &op, raw_ostream &os, const char *kind,
3353766ba44SKasper Nielsen     unsigned numVariadicGroups, unsigned numElements,
336fd407e1fSAlex Zinenko     llvm::function_ref<const NamedTypeConstraint &(const Operator &, int)>
337fd407e1fSAlex Zinenko         getElement) {
338bccd37f6SRahul Joshi   assert(llvm::is_contained(SmallVector<StringRef, 2>{"operand", "result"},
339bccd37f6SRahul Joshi                             kind) &&
340fd407e1fSAlex Zinenko          "unsupported kind");
341fd407e1fSAlex Zinenko 
342fd407e1fSAlex Zinenko   // Traits indicating how to process variadic elements.
343bccd37f6SRahul Joshi   std::string sameSizeTrait = formatv("::mlir::OpTrait::SameVariadic{0}{1}Size",
344bccd37f6SRahul Joshi                                       StringRef(kind).take_front().upper(),
345bccd37f6SRahul Joshi                                       StringRef(kind).drop_front());
346f9265de8SAlex Zinenko   std::string attrSizedTrait = attrSizedTraitForKind(kind);
347fd407e1fSAlex Zinenko 
34854c99842SMichal Terepeta   // If there is only one variable-length element group, its size can be
34954c99842SMichal Terepeta   // inferred from the total number of elements. If there are none, the
35054c99842SMichal Terepeta   // generation is straightforward.
3513766ba44SKasper Nielsen   if (numVariadicGroups <= 1) {
352fd407e1fSAlex Zinenko     bool seenVariableLength = false;
3533766ba44SKasper Nielsen     for (unsigned i = 0; i < numElements; ++i) {
354fd407e1fSAlex Zinenko       const NamedTypeConstraint &element = getElement(op, i);
355fd407e1fSAlex Zinenko       if (element.isVariableLength())
356fd407e1fSAlex Zinenko         seenVariableLength = true;
357fd407e1fSAlex Zinenko       if (element.name.empty())
358fd407e1fSAlex Zinenko         continue;
359fd407e1fSAlex Zinenko       if (element.isVariableLength()) {
360bccd37f6SRahul Joshi         os << formatv(element.isOptional() ? opOneOptionalTemplate
361fd407e1fSAlex Zinenko                                            : opOneVariadicTemplate,
3623766ba44SKasper Nielsen                       sanitizeName(element.name), kind, numElements, i);
363fd407e1fSAlex Zinenko       } else if (seenVariableLength) {
364bccd37f6SRahul Joshi         os << formatv(opSingleAfterVariableTemplate, sanitizeName(element.name),
365bccd37f6SRahul Joshi                       kind, numElements, i);
366fd407e1fSAlex Zinenko       } else {
367bccd37f6SRahul Joshi         os << formatv(opSingleTemplate, sanitizeName(element.name), kind, i);
368fd407e1fSAlex Zinenko       }
369fd407e1fSAlex Zinenko     }
370fd407e1fSAlex Zinenko     return;
371fd407e1fSAlex Zinenko   }
372fd407e1fSAlex Zinenko 
373fd407e1fSAlex Zinenko   // Handle the operations where variadic groups have the same size.
374fd407e1fSAlex Zinenko   if (op.getTrait(sameSizeTrait)) {
3753766ba44SKasper Nielsen     // Count the number of simple elements
3763766ba44SKasper Nielsen     unsigned numSimpleLength = 0;
3773766ba44SKasper Nielsen     for (unsigned i = 0; i < numElements; ++i) {
3783766ba44SKasper Nielsen       const NamedTypeConstraint &element = getElement(op, i);
3793766ba44SKasper Nielsen       if (!element.isVariableLength()) {
3803766ba44SKasper Nielsen         ++numSimpleLength;
3813766ba44SKasper Nielsen       }
3823766ba44SKasper Nielsen     }
3833766ba44SKasper Nielsen 
3843766ba44SKasper Nielsen     // Generate the accessors
385fd407e1fSAlex Zinenko     int numPrecedingSimple = 0;
386fd407e1fSAlex Zinenko     int numPrecedingVariadic = 0;
3873766ba44SKasper Nielsen     for (unsigned i = 0; i < numElements; ++i) {
388fd407e1fSAlex Zinenko       const NamedTypeConstraint &element = getElement(op, i);
389fd407e1fSAlex Zinenko       if (!element.name.empty()) {
390bccd37f6SRahul Joshi         os << formatv(opVariadicEqualPrefixTemplate, sanitizeName(element.name),
391bccd37f6SRahul Joshi                       kind, numSimpleLength, numVariadicGroups,
392bccd37f6SRahul Joshi                       numPrecedingSimple, numPrecedingVariadic);
393bccd37f6SRahul Joshi         os << formatv(element.isVariableLength()
394fd407e1fSAlex Zinenko                           ? opVariadicEqualVariadicTemplate
395fd407e1fSAlex Zinenko                           : opVariadicEqualSimpleTemplate,
396fd407e1fSAlex Zinenko                       kind);
397fd407e1fSAlex Zinenko       }
398fd407e1fSAlex Zinenko       if (element.isVariableLength())
399fd407e1fSAlex Zinenko         ++numPrecedingVariadic;
400fd407e1fSAlex Zinenko       else
401fd407e1fSAlex Zinenko         ++numPrecedingSimple;
402fd407e1fSAlex Zinenko     }
403fd407e1fSAlex Zinenko     return;
404fd407e1fSAlex Zinenko   }
405fd407e1fSAlex Zinenko 
406fd407e1fSAlex Zinenko   // Handle the operations where the size of groups (variadic or not) is
407fd407e1fSAlex Zinenko   // provided as an attribute. For non-variadic elements, make sure to return
408fd407e1fSAlex Zinenko   // an element rather than a singleton container.
409fd407e1fSAlex Zinenko   if (op.getTrait(attrSizedTrait)) {
4103766ba44SKasper Nielsen     for (unsigned i = 0; i < numElements; ++i) {
411fd407e1fSAlex Zinenko       const NamedTypeConstraint &element = getElement(op, i);
412fd407e1fSAlex Zinenko       if (element.name.empty())
413fd407e1fSAlex Zinenko         continue;
414fd407e1fSAlex Zinenko       std::string trailing;
415fd407e1fSAlex Zinenko       if (!element.isVariableLength())
416fd407e1fSAlex Zinenko         trailing = "[0]";
417fd407e1fSAlex Zinenko       else if (element.isOptional())
418fd407e1fSAlex Zinenko         trailing = std::string(
419bccd37f6SRahul Joshi             formatv(opVariadicSegmentOptionalTrailingTemplate, kind));
420bccd37f6SRahul Joshi       os << formatv(opVariadicSegmentTemplate, sanitizeName(element.name), kind,
421bccd37f6SRahul Joshi                     i, trailing);
422fd407e1fSAlex Zinenko     }
423fd407e1fSAlex Zinenko     return;
424fd407e1fSAlex Zinenko   }
425fd407e1fSAlex Zinenko 
426fd407e1fSAlex Zinenko   llvm::PrintFatalError("unsupported " + llvm::Twine(kind) + " structure");
427fd407e1fSAlex Zinenko }
428fd407e1fSAlex Zinenko 
429f9265de8SAlex Zinenko /// Free function helpers accessing Operator components.
430f9265de8SAlex Zinenko static int getNumOperands(const Operator &op) { return op.getNumOperands(); }
431f9265de8SAlex Zinenko static const NamedTypeConstraint &getOperand(const Operator &op, int i) {
432f9265de8SAlex Zinenko   return op.getOperand(i);
433f9265de8SAlex Zinenko }
434f9265de8SAlex Zinenko static int getNumResults(const Operator &op) { return op.getNumResults(); }
435f9265de8SAlex Zinenko static const NamedTypeConstraint &getResult(const Operator &op, int i) {
436f9265de8SAlex Zinenko   return op.getResult(i);
437f9265de8SAlex Zinenko }
438f9265de8SAlex Zinenko 
439c5a6712fSAlex Zinenko /// Emits accessors to Op operands.
440fd407e1fSAlex Zinenko static void emitOperandAccessors(const Operator &op, raw_ostream &os) {
4413766ba44SKasper Nielsen   emitElementAccessors(op, os, "operand", op.getNumVariableLengthOperands(),
4423766ba44SKasper Nielsen                        getNumOperands(op), getOperand);
443fd407e1fSAlex Zinenko }
444fd407e1fSAlex Zinenko 
445c5a6712fSAlex Zinenko /// Emits accessors Op results.
446fd407e1fSAlex Zinenko static void emitResultAccessors(const Operator &op, raw_ostream &os) {
4473766ba44SKasper Nielsen   emitElementAccessors(op, os, "result", op.getNumVariableLengthResults(),
4483766ba44SKasper Nielsen                        getNumResults(op), getResult);
449f9265de8SAlex Zinenko }
450f9265de8SAlex Zinenko 
451c5a6712fSAlex Zinenko /// Emits accessors to Op attributes.
45267a910bbSRahul Kayaith static void emitAttributeAccessors(const Operator &op, raw_ostream &os) {
453c5a6712fSAlex Zinenko   for (const auto &namedAttr : op.getAttributes()) {
454c5a6712fSAlex Zinenko     // Skip "derived" attributes because they are just C++ functions that we
455c5a6712fSAlex Zinenko     // don't currently expose.
456c5a6712fSAlex Zinenko     if (namedAttr.attr.isDerivedAttr())
457c5a6712fSAlex Zinenko       continue;
458c5a6712fSAlex Zinenko 
459c5a6712fSAlex Zinenko     if (namedAttr.name.empty())
460c5a6712fSAlex Zinenko       continue;
461c5a6712fSAlex Zinenko 
462029e199dSAlex Zinenko     std::string sanitizedName = sanitizeName(namedAttr.name);
463029e199dSAlex Zinenko 
464c5a6712fSAlex Zinenko     // Unit attributes are handled specially.
465dec8055aSKazu Hirata     if (namedAttr.attr.getStorageType().trim() == "::mlir::UnitAttr") {
466bccd37f6SRahul Joshi       os << formatv(unitAttributeGetterTemplate, sanitizedName, namedAttr.name);
467bccd37f6SRahul Joshi       os << formatv(unitAttributeSetterTemplate, sanitizedName, namedAttr.name);
468bccd37f6SRahul Joshi       os << formatv(attributeDeleterTemplate, sanitizedName, namedAttr.name);
469c5a6712fSAlex Zinenko       continue;
470c5a6712fSAlex Zinenko     }
471c5a6712fSAlex Zinenko 
472029e199dSAlex Zinenko     if (namedAttr.attr.isOptional()) {
473bccd37f6SRahul Joshi       os << formatv(optionalAttributeGetterTemplate, sanitizedName,
47467a910bbSRahul Kayaith                     namedAttr.name);
475bccd37f6SRahul Joshi       os << formatv(optionalAttributeSetterTemplate, sanitizedName,
476c5a6712fSAlex Zinenko                     namedAttr.name);
477bccd37f6SRahul Joshi       os << formatv(attributeDeleterTemplate, sanitizedName, namedAttr.name);
478029e199dSAlex Zinenko     } else {
479bccd37f6SRahul Joshi       os << formatv(attributeGetterTemplate, sanitizedName, namedAttr.name);
480bccd37f6SRahul Joshi       os << formatv(attributeSetterTemplate, sanitizedName, namedAttr.name);
481029e199dSAlex Zinenko       // Non-optional attributes cannot be deleted.
482029e199dSAlex Zinenko     }
483c5a6712fSAlex Zinenko   }
484c5a6712fSAlex Zinenko }
485c5a6712fSAlex Zinenko 
486f9265de8SAlex Zinenko /// Template for the default auto-generated builder.
48771b6b010SStella Laurenzo ///   {0} is a comma-separated list of builder arguments, including the trailing
488f9265de8SAlex Zinenko ///       `loc` and `ip`;
4898e6c55c9SStella Laurenzo ///   {1} is the code populating `operands`, `results` and `attributes`,
4908e6c55c9SStella Laurenzo ///       `successors` fields.
491f9265de8SAlex Zinenko constexpr const char *initTemplate = R"Py(
49271b6b010SStella Laurenzo   def __init__(self, {0}):
493f9265de8SAlex Zinenko     operands = []
494f9265de8SAlex Zinenko     results = []
495f9265de8SAlex Zinenko     attributes = {{}
49618fbd5feSAlex Zinenko     regions = None
49771b6b010SStella Laurenzo     {1}
498f4125e02SPeter Hawkins     super().__init__({2})
499f9265de8SAlex Zinenko )Py";
500f9265de8SAlex Zinenko 
501f9265de8SAlex Zinenko /// Template for appending a single element to the operand/result list.
502b164f23cSAlex Zinenko ///   {0} is the field name.
503*acde3f72SPeter Hawkins constexpr const char *singleOperandAppendTemplate = "operands.append({0})";
504b164f23cSAlex Zinenko constexpr const char *singleResultAppendTemplate = "results.append({0})";
505f9265de8SAlex Zinenko 
506f9265de8SAlex Zinenko /// Template for appending an optional element to the operand/result list.
507b164f23cSAlex Zinenko ///   {0} is the field name.
508b164f23cSAlex Zinenko constexpr const char *optionalAppendOperandTemplate =
509*acde3f72SPeter Hawkins     "if {0} is not None: operands.append({0})";
5106981e5ecSAlex Zinenko constexpr const char *optionalAppendAttrSizedOperandsTemplate =
511*acde3f72SPeter Hawkins     "operands.append({0})";
512b164f23cSAlex Zinenko constexpr const char *optionalAppendResultTemplate =
513b164f23cSAlex Zinenko     "if {0} is not None: results.append({0})";
514f9265de8SAlex Zinenko 
515b164f23cSAlex Zinenko /// Template for appending a list of elements to the operand/result list.
516b164f23cSAlex Zinenko ///   {0} is the field name.
517b164f23cSAlex Zinenko constexpr const char *multiOperandAppendTemplate =
518b164f23cSAlex Zinenko     "operands.extend(_get_op_results_or_values({0}))";
519b164f23cSAlex Zinenko constexpr const char *multiOperandAppendPackTemplate =
520b164f23cSAlex Zinenko     "operands.append(_get_op_results_or_values({0}))";
521b164f23cSAlex Zinenko constexpr const char *multiResultAppendTemplate = "results.extend({0})";
522f9265de8SAlex Zinenko 
523b57acb9aSJacques Pienaar /// Template for attribute builder from raw input in the operation builder.
524b57acb9aSJacques Pienaar ///   {0} is the builder argument name;
525b57acb9aSJacques Pienaar ///   {1} is the attribute builder from raw;
526b57acb9aSJacques Pienaar ///   {2} is the attribute builder from raw.
527b57acb9aSJacques Pienaar /// Use the value the user passed in if either it is already an Attribute or
528b57acb9aSJacques Pienaar /// there is no method registered to make it an Attribute.
529b57acb9aSJacques Pienaar constexpr const char *initAttributeWithBuilderTemplate =
530b57acb9aSJacques Pienaar     R"Py(attributes["{1}"] = ({0} if (
53135593f66SSergei Lebedev     isinstance({0}, _ods_ir.Attribute) or
532b57acb9aSJacques Pienaar     not _ods_ir.AttrBuilder.contains('{2}')) else
533b57acb9aSJacques Pienaar       _ods_ir.AttrBuilder.get('{2}')({0}, context=_ods_context)))Py";
534c5a6712fSAlex Zinenko 
535b57acb9aSJacques Pienaar /// Template for attribute builder from raw input for optional attribute in the
536b57acb9aSJacques Pienaar /// operation builder.
537b57acb9aSJacques Pienaar ///   {0} is the builder argument name;
538b57acb9aSJacques Pienaar ///   {1} is the attribute builder from raw;
539b57acb9aSJacques Pienaar ///   {2} is the attribute builder from raw.
540b57acb9aSJacques Pienaar /// Use the value the user passed in if either it is already an Attribute or
541b57acb9aSJacques Pienaar /// there is no method registered to make it an Attribute.
542b57acb9aSJacques Pienaar constexpr const char *initOptionalAttributeWithBuilderTemplate =
543b57acb9aSJacques Pienaar     R"Py(if {0} is not None: attributes["{1}"] = ({0} if (
54435593f66SSergei Lebedev         isinstance({0}, _ods_ir.Attribute) or
545b57acb9aSJacques Pienaar         not _ods_ir.AttrBuilder.contains('{2}')) else
546b57acb9aSJacques Pienaar           _ods_ir.AttrBuilder.get('{2}')({0}, context=_ods_context)))Py";
547c5a6712fSAlex Zinenko 
548c5a6712fSAlex Zinenko constexpr const char *initUnitAttributeTemplate =
5498a1f1a10SStella Laurenzo     R"Py(if bool({1}): attributes["{0}"] = _ods_ir.UnitAttr.get(
5508a1f1a10SStella Laurenzo       _ods_get_default_loc_context(loc)))Py";
551c5a6712fSAlex Zinenko 
5528e6c55c9SStella Laurenzo /// Template to initialize the successors list in the builder if there are any
5538e6c55c9SStella Laurenzo /// successors.
5548e6c55c9SStella Laurenzo ///   {0} is the value to initialize the successors list to.
5558e6c55c9SStella Laurenzo constexpr const char *initSuccessorsTemplate = R"Py(_ods_successors = {0})Py";
5568e6c55c9SStella Laurenzo 
5578e6c55c9SStella Laurenzo /// Template to append or extend the list of successors in the builder.
5588e6c55c9SStella Laurenzo ///   {0} is the list method ('append' or 'extend');
5598e6c55c9SStella Laurenzo ///   {1} is the value to add.
5608e6c55c9SStella Laurenzo constexpr const char *addSuccessorTemplate = R"Py(_ods_successors.{0}({1}))Py";
5618e6c55c9SStella Laurenzo 
5622995d29bSAlex Zinenko /// Returns true if the SameArgumentAndResultTypes trait can be used to infer
5632995d29bSAlex Zinenko /// result types of the given operation.
5642995d29bSAlex Zinenko static bool hasSameArgumentAndResultTypes(const Operator &op) {
5652995d29bSAlex Zinenko   return op.getTrait("::mlir::OpTrait::SameOperandsAndResultType") &&
5662995d29bSAlex Zinenko          op.getNumVariableLengthResults() == 0;
5672995d29bSAlex Zinenko }
5682995d29bSAlex Zinenko 
5692995d29bSAlex Zinenko /// Returns true if the FirstAttrDerivedResultType trait can be used to infer
5702995d29bSAlex Zinenko /// result types of the given operation.
5712995d29bSAlex Zinenko static bool hasFirstAttrDerivedResultTypes(const Operator &op) {
5722995d29bSAlex Zinenko   return op.getTrait("::mlir::OpTrait::FirstAttrDerivedResultType") &&
5732995d29bSAlex Zinenko          op.getNumVariableLengthResults() == 0;
5742995d29bSAlex Zinenko }
5752995d29bSAlex Zinenko 
5762995d29bSAlex Zinenko /// Returns true if the InferTypeOpInterface can be used to infer result types
5772995d29bSAlex Zinenko /// of the given operation.
5782995d29bSAlex Zinenko static bool hasInferTypeInterface(const Operator &op) {
5792995d29bSAlex Zinenko   return op.getTrait("::mlir::InferTypeOpInterface::Trait") &&
5802995d29bSAlex Zinenko          op.getNumRegions() == 0;
5812995d29bSAlex Zinenko }
5822995d29bSAlex Zinenko 
5832995d29bSAlex Zinenko /// Returns true if there is a trait or interface that can be used to infer
5842995d29bSAlex Zinenko /// result types of the given operation.
5852995d29bSAlex Zinenko static bool canInferType(const Operator &op) {
5862995d29bSAlex Zinenko   return hasSameArgumentAndResultTypes(op) ||
5872995d29bSAlex Zinenko          hasFirstAttrDerivedResultTypes(op) || hasInferTypeInterface(op);
5882995d29bSAlex Zinenko }
5892995d29bSAlex Zinenko 
5902995d29bSAlex Zinenko /// Populates `builderArgs` with result names if the builder is expected to
5912995d29bSAlex Zinenko /// accept them as arguments.
592c5a6712fSAlex Zinenko static void
5932995d29bSAlex Zinenko populateBuilderArgsResults(const Operator &op,
594bccd37f6SRahul Joshi                            SmallVectorImpl<std::string> &builderArgs) {
5952995d29bSAlex Zinenko   if (canInferType(op))
5962995d29bSAlex Zinenko     return;
5972995d29bSAlex Zinenko 
598c5a6712fSAlex Zinenko   for (int i = 0, e = op.getNumResults(); i < e; ++i) {
599c5a6712fSAlex Zinenko     std::string name = op.getResultName(i).str();
600fd226c9bSStella Laurenzo     if (name.empty()) {
601fd226c9bSStella Laurenzo       if (op.getNumResults() == 1) {
602fd226c9bSStella Laurenzo         // Special case for one result, make the default name be 'result'
603fd226c9bSStella Laurenzo         // to properly match the built-in result accessor.
604fd226c9bSStella Laurenzo         name = "result";
605fd226c9bSStella Laurenzo       } else {
606bccd37f6SRahul Joshi         name = formatv("_gen_res_{0}", i);
607fd226c9bSStella Laurenzo       }
608fd226c9bSStella Laurenzo     }
609c5a6712fSAlex Zinenko     name = sanitizeName(name);
610c5a6712fSAlex Zinenko     builderArgs.push_back(name);
611c5a6712fSAlex Zinenko   }
6122995d29bSAlex Zinenko }
6132995d29bSAlex Zinenko 
6142995d29bSAlex Zinenko /// Populates `builderArgs` with the Python-compatible names of builder function
6152995d29bSAlex Zinenko /// arguments using intermixed attributes and operands in the same order as they
6162995d29bSAlex Zinenko /// appear in the `arguments` field of the op definition. Additionally,
6172995d29bSAlex Zinenko /// `operandNames` is populated with names of operands in their order of
6182995d29bSAlex Zinenko /// appearance.
619bccd37f6SRahul Joshi static void populateBuilderArgs(const Operator &op,
620bccd37f6SRahul Joshi                                 SmallVectorImpl<std::string> &builderArgs,
621bccd37f6SRahul Joshi                                 SmallVectorImpl<std::string> &operandNames) {
622c5a6712fSAlex Zinenko   for (int i = 0, e = op.getNumArgs(); i < e; ++i) {
623c5a6712fSAlex Zinenko     std::string name = op.getArgName(i).str();
624c5a6712fSAlex Zinenko     if (name.empty())
625bccd37f6SRahul Joshi       name = formatv("_gen_arg_{0}", i);
626c5a6712fSAlex Zinenko     name = sanitizeName(name);
627c5a6712fSAlex Zinenko     builderArgs.push_back(name);
628b0746c68SKazu Hirata     if (!isa<NamedAttribute *>(op.getArg(i)))
629c5a6712fSAlex Zinenko       operandNames.push_back(name);
630c5a6712fSAlex Zinenko   }
6319b79f50bSJeremy Furtek }
6329b79f50bSJeremy Furtek 
6339b79f50bSJeremy Furtek /// Populates `builderArgs` with the Python-compatible names of builder function
6349b79f50bSJeremy Furtek /// successor arguments. Additionally, `successorArgNames` is also populated.
635bccd37f6SRahul Joshi static void
636bccd37f6SRahul Joshi populateBuilderArgsSuccessors(const Operator &op,
637bccd37f6SRahul Joshi                               SmallVectorImpl<std::string> &builderArgs,
638bccd37f6SRahul Joshi                               SmallVectorImpl<std::string> &successorArgNames) {
6398e6c55c9SStella Laurenzo 
6408e6c55c9SStella Laurenzo   for (int i = 0, e = op.getNumSuccessors(); i < e; ++i) {
6418e6c55c9SStella Laurenzo     NamedSuccessor successor = op.getSuccessor(i);
6428e6c55c9SStella Laurenzo     std::string name = std::string(successor.name);
6438e6c55c9SStella Laurenzo     if (name.empty())
644bccd37f6SRahul Joshi       name = formatv("_gen_successor_{0}", i);
6458e6c55c9SStella Laurenzo     name = sanitizeName(name);
6468e6c55c9SStella Laurenzo     builderArgs.push_back(name);
6478e6c55c9SStella Laurenzo     successorArgNames.push_back(name);
6488e6c55c9SStella Laurenzo   }
649c5a6712fSAlex Zinenko }
650c5a6712fSAlex Zinenko 
651c5a6712fSAlex Zinenko /// Populates `builderLines` with additional lines that are required in the
652c5a6712fSAlex Zinenko /// builder to set up operation attributes. `argNames` is expected to contain
653c5a6712fSAlex Zinenko /// the names of builder arguments that correspond to op arguments, i.e. to the
654c5a6712fSAlex Zinenko /// operands and attributes in the same order as they appear in the `arguments`
655c5a6712fSAlex Zinenko /// field.
656c5a6712fSAlex Zinenko static void
657bccd37f6SRahul Joshi populateBuilderLinesAttr(const Operator &op, ArrayRef<std::string> argNames,
658bccd37f6SRahul Joshi                          SmallVectorImpl<std::string> &builderLines) {
659b57acb9aSJacques Pienaar   builderLines.push_back("_ods_context = _ods_get_default_loc_context(loc)");
660c5a6712fSAlex Zinenko   for (int i = 0, e = op.getNumArgs(); i < e; ++i) {
661c5a6712fSAlex Zinenko     Argument arg = op.getArg(i);
66268f58812STres Popp     auto *attribute = llvm::dyn_cast_if_present<NamedAttribute *>(arg);
663c5a6712fSAlex Zinenko     if (!attribute)
664c5a6712fSAlex Zinenko       continue;
665c5a6712fSAlex Zinenko 
666c5a6712fSAlex Zinenko     // Unit attributes are handled specially.
667dec8055aSKazu Hirata     if (attribute->attr.getStorageType().trim() == "::mlir::UnitAttr") {
668bccd37f6SRahul Joshi       builderLines.push_back(
669bccd37f6SRahul Joshi           formatv(initUnitAttributeTemplate, attribute->name, argNames[i]));
670c5a6712fSAlex Zinenko       continue;
671c5a6712fSAlex Zinenko     }
672c5a6712fSAlex Zinenko 
673bccd37f6SRahul Joshi     builderLines.push_back(formatv(
674b57acb9aSJacques Pienaar         attribute->attr.isOptional() || attribute->attr.hasDefaultValue()
675b57acb9aSJacques Pienaar             ? initOptionalAttributeWithBuilderTemplate
676b57acb9aSJacques Pienaar             : initAttributeWithBuilderTemplate,
677b57acb9aSJacques Pienaar         argNames[i], attribute->name, attribute->attr.getAttrDefName()));
678c5a6712fSAlex Zinenko   }
679c5a6712fSAlex Zinenko }
680c5a6712fSAlex Zinenko 
681c5a6712fSAlex Zinenko /// Populates `builderLines` with additional lines that are required in the
6828e6c55c9SStella Laurenzo /// builder to set up successors. successorArgNames is expected to correspond
6838e6c55c9SStella Laurenzo /// to the Python argument name for each successor on the op.
684bccd37f6SRahul Joshi static void
685bccd37f6SRahul Joshi populateBuilderLinesSuccessors(const Operator &op,
686bccd37f6SRahul Joshi                                ArrayRef<std::string> successorArgNames,
687bccd37f6SRahul Joshi                                SmallVectorImpl<std::string> &builderLines) {
6888e6c55c9SStella Laurenzo   if (successorArgNames.empty()) {
689bccd37f6SRahul Joshi     builderLines.push_back(formatv(initSuccessorsTemplate, "None"));
6908e6c55c9SStella Laurenzo     return;
6918e6c55c9SStella Laurenzo   }
6928e6c55c9SStella Laurenzo 
693bccd37f6SRahul Joshi   builderLines.push_back(formatv(initSuccessorsTemplate, "[]"));
6948e6c55c9SStella Laurenzo   for (int i = 0, e = successorArgNames.size(); i < e; ++i) {
6958e6c55c9SStella Laurenzo     auto &argName = successorArgNames[i];
6968e6c55c9SStella Laurenzo     const NamedSuccessor &successor = op.getSuccessor(i);
697bccd37f6SRahul Joshi     builderLines.push_back(formatv(addSuccessorTemplate,
698bccd37f6SRahul Joshi                                    successor.isVariadic() ? "extend" : "append",
699bccd37f6SRahul Joshi                                    argName));
7008e6c55c9SStella Laurenzo   }
7018e6c55c9SStella Laurenzo }
7028e6c55c9SStella Laurenzo 
7038e6c55c9SStella Laurenzo /// Populates `builderLines` with additional lines that are required in the
704b164f23cSAlex Zinenko /// builder to set up op operands.
705b164f23cSAlex Zinenko static void
706bccd37f6SRahul Joshi populateBuilderLinesOperand(const Operator &op, ArrayRef<std::string> names,
707bccd37f6SRahul Joshi                             SmallVectorImpl<std::string> &builderLines) {
708b164f23cSAlex Zinenko   bool sizedSegments = op.getTrait(attrSizedTraitForKind("operand")) != nullptr;
709f9265de8SAlex Zinenko 
710f9265de8SAlex Zinenko   // For each element, find or generate a name.
711b164f23cSAlex Zinenko   for (int i = 0, e = op.getNumOperands(); i < e; ++i) {
712b164f23cSAlex Zinenko     const NamedTypeConstraint &element = op.getOperand(i);
713c5a6712fSAlex Zinenko     std::string name = names[i];
714f9265de8SAlex Zinenko 
715f9265de8SAlex Zinenko     // Choose the formatting string based on the element kind.
716bccd37f6SRahul Joshi     StringRef formatString;
717f9265de8SAlex Zinenko     if (!element.isVariableLength()) {
718b164f23cSAlex Zinenko       formatString = singleOperandAppendTemplate;
719f9265de8SAlex Zinenko     } else if (element.isOptional()) {
7206981e5ecSAlex Zinenko       if (sizedSegments) {
7216981e5ecSAlex Zinenko         formatString = optionalAppendAttrSizedOperandsTemplate;
7226981e5ecSAlex Zinenko       } else {
723b164f23cSAlex Zinenko         formatString = optionalAppendOperandTemplate;
7246981e5ecSAlex Zinenko       }
725f9265de8SAlex Zinenko     } else {
726f9265de8SAlex Zinenko       assert(element.isVariadic() && "unhandled element group type");
727b164f23cSAlex Zinenko       // If emitting with sizedSegments, then we add the actual list-typed
728b164f23cSAlex Zinenko       // element. Otherwise, we extend the actual operands.
72971b6b010SStella Laurenzo       if (sizedSegments) {
730b164f23cSAlex Zinenko         formatString = multiOperandAppendPackTemplate;
73171b6b010SStella Laurenzo       } else {
732b164f23cSAlex Zinenko         formatString = multiOperandAppendTemplate;
73371b6b010SStella Laurenzo       }
734f9265de8SAlex Zinenko     }
735f9265de8SAlex Zinenko 
736bccd37f6SRahul Joshi     builderLines.push_back(formatv(formatString.data(), name));
737b164f23cSAlex Zinenko   }
738b164f23cSAlex Zinenko }
739b164f23cSAlex Zinenko 
7402995d29bSAlex Zinenko /// Python code template for deriving the operation result types from its
7412995d29bSAlex Zinenko /// attribute:
7422995d29bSAlex Zinenko ///   - {0} is the name of the attribute from which to derive the types.
7432995d29bSAlex Zinenko constexpr const char *deriveTypeFromAttrTemplate =
74427c6d55cSMaksim Levental     R"Py(_ods_result_type_source_attr = attributes["{0}"]
7452995d29bSAlex Zinenko _ods_derived_result_type = (
7462995d29bSAlex Zinenko     _ods_ir.TypeAttr(_ods_result_type_source_attr).value
7472995d29bSAlex Zinenko     if _ods_ir.TypeAttr.isinstance(_ods_result_type_source_attr) else
74827c6d55cSMaksim Levental     _ods_result_type_source_attr.type))Py";
7492995d29bSAlex Zinenko 
7502995d29bSAlex Zinenko /// Python code template appending {0} type {1} times to the results list.
7512995d29bSAlex Zinenko constexpr const char *appendSameResultsTemplate = "results.extend([{0}] * {1})";
7522995d29bSAlex Zinenko 
7532995d29bSAlex Zinenko /// Appends the given multiline string as individual strings into
7542995d29bSAlex Zinenko /// `builderLines`.
7552995d29bSAlex Zinenko static void appendLineByLine(StringRef string,
756bccd37f6SRahul Joshi                              SmallVectorImpl<std::string> &builderLines) {
7572995d29bSAlex Zinenko 
7582995d29bSAlex Zinenko   std::pair<StringRef, StringRef> split = std::make_pair(string, string);
7592995d29bSAlex Zinenko   do {
7602995d29bSAlex Zinenko     split = split.second.split('\n');
7612995d29bSAlex Zinenko     builderLines.push_back(split.first.str());
7622995d29bSAlex Zinenko   } while (!split.second.empty());
7632995d29bSAlex Zinenko }
7642995d29bSAlex Zinenko 
765b164f23cSAlex Zinenko /// Populates `builderLines` with additional lines that are required in the
766b164f23cSAlex Zinenko /// builder to set up op results.
767b164f23cSAlex Zinenko static void
768bccd37f6SRahul Joshi populateBuilderLinesResult(const Operator &op, ArrayRef<std::string> names,
769bccd37f6SRahul Joshi                            SmallVectorImpl<std::string> &builderLines) {
770b164f23cSAlex Zinenko   bool sizedSegments = op.getTrait(attrSizedTraitForKind("result")) != nullptr;
771b164f23cSAlex Zinenko 
7722995d29bSAlex Zinenko   if (hasSameArgumentAndResultTypes(op)) {
773bccd37f6SRahul Joshi     builderLines.push_back(formatv(appendSameResultsTemplate,
774bccd37f6SRahul Joshi                                    "operands[0].type", op.getNumResults()));
7752995d29bSAlex Zinenko     return;
7762995d29bSAlex Zinenko   }
7772995d29bSAlex Zinenko 
7782995d29bSAlex Zinenko   if (hasFirstAttrDerivedResultTypes(op)) {
7792995d29bSAlex Zinenko     const NamedAttribute &firstAttr = op.getAttribute(0);
7802995d29bSAlex Zinenko     assert(!firstAttr.name.empty() && "unexpected empty name for the attribute "
7812995d29bSAlex Zinenko                                       "from which the type is derived");
782bccd37f6SRahul Joshi     appendLineByLine(formatv(deriveTypeFromAttrTemplate, firstAttr.name).str(),
7832995d29bSAlex Zinenko                      builderLines);
784bccd37f6SRahul Joshi     builderLines.push_back(formatv(appendSameResultsTemplate,
7852995d29bSAlex Zinenko                                    "_ods_derived_result_type",
7862995d29bSAlex Zinenko                                    op.getNumResults()));
7872995d29bSAlex Zinenko     return;
7882995d29bSAlex Zinenko   }
7892995d29bSAlex Zinenko 
790f573bc24SJacques Pienaar   if (hasInferTypeInterface(op))
7912995d29bSAlex Zinenko     return;
7922995d29bSAlex Zinenko 
793b164f23cSAlex Zinenko   // For each element, find or generate a name.
794b164f23cSAlex Zinenko   for (int i = 0, e = op.getNumResults(); i < e; ++i) {
795b164f23cSAlex Zinenko     const NamedTypeConstraint &element = op.getResult(i);
796b164f23cSAlex Zinenko     std::string name = names[i];
797b164f23cSAlex Zinenko 
798b164f23cSAlex Zinenko     // Choose the formatting string based on the element kind.
799bccd37f6SRahul Joshi     StringRef formatString;
800b164f23cSAlex Zinenko     if (!element.isVariableLength()) {
801b164f23cSAlex Zinenko       formatString = singleResultAppendTemplate;
802b164f23cSAlex Zinenko     } else if (element.isOptional()) {
803b164f23cSAlex Zinenko       formatString = optionalAppendResultTemplate;
804b164f23cSAlex Zinenko     } else {
805b164f23cSAlex Zinenko       assert(element.isVariadic() && "unhandled element group type");
806b164f23cSAlex Zinenko       // If emitting with sizedSegments, then we add the actual list-typed
807b164f23cSAlex Zinenko       // element. Otherwise, we extend the actual operands.
808b164f23cSAlex Zinenko       if (sizedSegments) {
809b164f23cSAlex Zinenko         formatString = singleResultAppendTemplate;
810b164f23cSAlex Zinenko       } else {
811b164f23cSAlex Zinenko         formatString = multiResultAppendTemplate;
812b164f23cSAlex Zinenko       }
813b164f23cSAlex Zinenko     }
814b164f23cSAlex Zinenko 
815bccd37f6SRahul Joshi     builderLines.push_back(formatv(formatString.data(), name));
816f9265de8SAlex Zinenko   }
817f9265de8SAlex Zinenko }
818f9265de8SAlex Zinenko 
81918fbd5feSAlex Zinenko /// If the operation has variadic regions, adds a builder argument to specify
82018fbd5feSAlex Zinenko /// the number of those regions and builder lines to forward it to the generic
82118fbd5feSAlex Zinenko /// constructor.
822bccd37f6SRahul Joshi static void populateBuilderRegions(const Operator &op,
823bccd37f6SRahul Joshi                                    SmallVectorImpl<std::string> &builderArgs,
824bccd37f6SRahul Joshi                                    SmallVectorImpl<std::string> &builderLines) {
82518fbd5feSAlex Zinenko   if (op.hasNoVariadicRegions())
82618fbd5feSAlex Zinenko     return;
82718fbd5feSAlex Zinenko 
82818fbd5feSAlex Zinenko   // This is currently enforced when Operator is constructed.
82918fbd5feSAlex Zinenko   assert(op.getNumVariadicRegions() == 1 &&
83018fbd5feSAlex Zinenko          op.getRegion(op.getNumRegions() - 1).isVariadic() &&
83118fbd5feSAlex Zinenko          "expected the last region to be varidic");
83218fbd5feSAlex Zinenko 
83318fbd5feSAlex Zinenko   const NamedRegion &region = op.getRegion(op.getNumRegions() - 1);
83418fbd5feSAlex Zinenko   std::string name =
83518fbd5feSAlex Zinenko       ("num_" + region.name.take_front().lower() + region.name.drop_front())
83618fbd5feSAlex Zinenko           .str();
83718fbd5feSAlex Zinenko   builderArgs.push_back(name);
83818fbd5feSAlex Zinenko   builderLines.push_back(
839bccd37f6SRahul Joshi       formatv("regions = {0} + {1}", op.getNumRegions() - 1, name));
84018fbd5feSAlex Zinenko }
84118fbd5feSAlex Zinenko 
842f9265de8SAlex Zinenko /// Emits a default builder constructing an operation from the list of its
84327c6d55cSMaksim Levental /// result types, followed by a list of its operands. Returns vector
84427c6d55cSMaksim Levental /// of fully built functionArgs for downstream users (to save having to
84527c6d55cSMaksim Levental /// rebuild anew).
846bccd37f6SRahul Joshi static SmallVector<std::string> emitDefaultOpBuilder(const Operator &op,
84727c6d55cSMaksim Levental                                                      raw_ostream &os) {
848bccd37f6SRahul Joshi   SmallVector<std::string> builderArgs;
849bccd37f6SRahul Joshi   SmallVector<std::string> builderLines;
850bccd37f6SRahul Joshi   SmallVector<std::string> operandArgNames;
851bccd37f6SRahul Joshi   SmallVector<std::string> successorArgNames;
852c5a6712fSAlex Zinenko   builderArgs.reserve(op.getNumOperands() + op.getNumResults() +
8538e6c55c9SStella Laurenzo                       op.getNumNativeAttributes() + op.getNumSuccessors());
8542995d29bSAlex Zinenko   populateBuilderArgsResults(op, builderArgs);
8552995d29bSAlex Zinenko   size_t numResultArgs = builderArgs.size();
85627c6d55cSMaksim Levental   populateBuilderArgs(op, builderArgs, operandArgNames);
8579b79f50bSJeremy Furtek   size_t numOperandAttrArgs = builderArgs.size() - numResultArgs;
8589b79f50bSJeremy Furtek   populateBuilderArgsSuccessors(op, builderArgs, successorArgNames);
8598e6c55c9SStella Laurenzo 
860b164f23cSAlex Zinenko   populateBuilderLinesOperand(op, operandArgNames, builderLines);
861bccd37f6SRahul Joshi   populateBuilderLinesAttr(op, ArrayRef(builderArgs).drop_front(numResultArgs),
862bccd37f6SRahul Joshi                            builderLines);
8632995d29bSAlex Zinenko   populateBuilderLinesResult(
864bccd37f6SRahul Joshi       op, ArrayRef(builderArgs).take_front(numResultArgs), builderLines);
8658e6c55c9SStella Laurenzo   populateBuilderLinesSuccessors(op, successorArgNames, builderLines);
86618fbd5feSAlex Zinenko   populateBuilderRegions(op, builderArgs, builderLines);
867f9265de8SAlex Zinenko 
8689b79f50bSJeremy Furtek   // Layout of builderArgs vector elements:
8699b79f50bSJeremy Furtek   // [ result_args  operand_attr_args successor_args regions ]
8709b79f50bSJeremy Furtek 
8719b79f50bSJeremy Furtek   // Determine whether the argument corresponding to a given index into the
8729b79f50bSJeremy Furtek   // builderArgs vector is a python keyword argument or not.
8739b79f50bSJeremy Furtek   auto isKeywordArgFn = [&](size_t builderArgIndex) -> bool {
8749b79f50bSJeremy Furtek     // All result, successor, and region arguments are positional arguments.
8759b79f50bSJeremy Furtek     if ((builderArgIndex < numResultArgs) ||
8769b79f50bSJeremy Furtek         (builderArgIndex >= (numResultArgs + numOperandAttrArgs)))
8779b79f50bSJeremy Furtek       return false;
8789b79f50bSJeremy Furtek     // Keyword arguments:
8799b79f50bSJeremy Furtek     // - optional named attributes (including unit attributes)
8809b79f50bSJeremy Furtek     // - default-valued named attributes
8819b79f50bSJeremy Furtek     // - optional operands
8829b79f50bSJeremy Furtek     Argument a = op.getArg(builderArgIndex - numResultArgs);
88368f58812STres Popp     if (auto *nattr = llvm::dyn_cast_if_present<NamedAttribute *>(a))
8849b79f50bSJeremy Furtek       return (nattr->attr.isOptional() || nattr->attr.hasDefaultValue());
88568f58812STres Popp     if (auto *ntype = llvm::dyn_cast_if_present<NamedTypeConstraint *>(a))
8869b79f50bSJeremy Furtek       return ntype->isOptional();
8879b79f50bSJeremy Furtek     return false;
8889b79f50bSJeremy Furtek   };
8899b79f50bSJeremy Furtek 
8909b79f50bSJeremy Furtek   // StringRefs in functionArgs refer to strings allocated by builderArgs.
891bccd37f6SRahul Joshi   SmallVector<StringRef> functionArgs;
8929b79f50bSJeremy Furtek 
8939b79f50bSJeremy Furtek   // Add positional arguments.
8949b79f50bSJeremy Furtek   for (size_t i = 0, cnt = builderArgs.size(); i < cnt; ++i) {
8959b79f50bSJeremy Furtek     if (!isKeywordArgFn(i))
8969b79f50bSJeremy Furtek       functionArgs.push_back(builderArgs[i]);
8979b79f50bSJeremy Furtek   }
8989b79f50bSJeremy Furtek 
8999b79f50bSJeremy Furtek   // Add a bare '*' to indicate that all following arguments must be keyword
9009b79f50bSJeremy Furtek   // arguments.
9019b79f50bSJeremy Furtek   functionArgs.push_back("*");
9029b79f50bSJeremy Furtek 
9039b79f50bSJeremy Furtek   // Add a default 'None' value to each keyword arg string, and then add to the
9049b79f50bSJeremy Furtek   // function args list.
9059b79f50bSJeremy Furtek   for (size_t i = 0, cnt = builderArgs.size(); i < cnt; ++i) {
9069b79f50bSJeremy Furtek     if (isKeywordArgFn(i)) {
9079b79f50bSJeremy Furtek       builderArgs[i].append("=None");
9089b79f50bSJeremy Furtek       functionArgs.push_back(builderArgs[i]);
9099b79f50bSJeremy Furtek     }
9109b79f50bSJeremy Furtek   }
9119b79f50bSJeremy Furtek   functionArgs.push_back("loc=None");
9129b79f50bSJeremy Furtek   functionArgs.push_back("ip=None");
913f573bc24SJacques Pienaar 
914f573bc24SJacques Pienaar   SmallVector<std::string> initArgs;
915f4125e02SPeter Hawkins   initArgs.push_back("self.OPERATION_NAME");
916f4125e02SPeter Hawkins   initArgs.push_back("self._ODS_REGIONS");
917f4125e02SPeter Hawkins   initArgs.push_back("self._ODS_OPERAND_SEGMENTS");
918f4125e02SPeter Hawkins   initArgs.push_back("self._ODS_RESULT_SEGMENTS");
919f573bc24SJacques Pienaar   initArgs.push_back("attributes=attributes");
920f573bc24SJacques Pienaar   if (!hasInferTypeInterface(op))
921f573bc24SJacques Pienaar     initArgs.push_back("results=results");
922f573bc24SJacques Pienaar   initArgs.push_back("operands=operands");
923f573bc24SJacques Pienaar   initArgs.push_back("successors=_ods_successors");
924f573bc24SJacques Pienaar   initArgs.push_back("regions=regions");
925f573bc24SJacques Pienaar   initArgs.push_back("loc=loc");
926f573bc24SJacques Pienaar   initArgs.push_back("ip=ip");
927f573bc24SJacques Pienaar 
928bccd37f6SRahul Joshi   os << formatv(initTemplate, llvm::join(functionArgs, ", "),
929bccd37f6SRahul Joshi                 llvm::join(builderLines, "\n    "), llvm::join(initArgs, ", "));
93027c6d55cSMaksim Levental   return llvm::to_vector<8>(
931bccd37f6SRahul Joshi       llvm::map_range(functionArgs, [](StringRef s) { return s.str(); }));
932fd407e1fSAlex Zinenko }
933fd407e1fSAlex Zinenko 
93471b6b010SStella Laurenzo static void emitSegmentSpec(
93571b6b010SStella Laurenzo     const Operator &op, const char *kind,
93671b6b010SStella Laurenzo     llvm::function_ref<int(const Operator &)> getNumElements,
93771b6b010SStella Laurenzo     llvm::function_ref<const NamedTypeConstraint &(const Operator &, int)>
93871b6b010SStella Laurenzo         getElement,
93971b6b010SStella Laurenzo     raw_ostream &os) {
94071b6b010SStella Laurenzo   std::string segmentSpec("[");
94171b6b010SStella Laurenzo   for (int i = 0, e = getNumElements(op); i < e; ++i) {
94271b6b010SStella Laurenzo     const NamedTypeConstraint &element = getElement(op, i);
9436981e5ecSAlex Zinenko     if (element.isOptional()) {
94471b6b010SStella Laurenzo       segmentSpec.append("0,");
9456981e5ecSAlex Zinenko     } else if (element.isVariadic()) {
9466981e5ecSAlex Zinenko       segmentSpec.append("-1,");
94771b6b010SStella Laurenzo     } else {
94871b6b010SStella Laurenzo       segmentSpec.append("1,");
94971b6b010SStella Laurenzo     }
95071b6b010SStella Laurenzo   }
95171b6b010SStella Laurenzo   segmentSpec.append("]");
95271b6b010SStella Laurenzo 
953bccd37f6SRahul Joshi   os << formatv(opClassSizedSegmentsTemplate, kind, segmentSpec);
95471b6b010SStella Laurenzo }
95571b6b010SStella Laurenzo 
95671b6b010SStella Laurenzo static void emitRegionAttributes(const Operator &op, raw_ostream &os) {
95771b6b010SStella Laurenzo   // Emit _ODS_REGIONS = (min_region_count, has_no_variadic_regions).
95871b6b010SStella Laurenzo   // Note that the base OpView class defines this as (0, True).
95971b6b010SStella Laurenzo   unsigned minRegionCount = op.getNumRegions() - op.getNumVariadicRegions();
960bccd37f6SRahul Joshi   os << formatv(opClassRegionSpecTemplate, minRegionCount,
96171b6b010SStella Laurenzo                 op.hasNoVariadicRegions() ? "True" : "False");
96271b6b010SStella Laurenzo }
96371b6b010SStella Laurenzo 
96418fbd5feSAlex Zinenko /// Emits named accessors to regions.
96518fbd5feSAlex Zinenko static void emitRegionAccessors(const Operator &op, raw_ostream &os) {
96689de9cc8SMehdi Amini   for (const auto &en : llvm::enumerate(op.getRegions())) {
96718fbd5feSAlex Zinenko     const NamedRegion &region = en.value();
96818fbd5feSAlex Zinenko     if (region.name.empty())
96918fbd5feSAlex Zinenko       continue;
97018fbd5feSAlex Zinenko 
97118fbd5feSAlex Zinenko     assert((!region.isVariadic() || en.index() == op.getNumRegions() - 1) &&
97218fbd5feSAlex Zinenko            "expected only the last region to be variadic");
973bccd37f6SRahul Joshi     os << formatv(regionAccessorTemplate, sanitizeName(region.name),
97418fbd5feSAlex Zinenko                   std::to_string(en.index()) +
97518fbd5feSAlex Zinenko                       (region.isVariadic() ? ":" : ""));
97618fbd5feSAlex Zinenko   }
97718fbd5feSAlex Zinenko }
97818fbd5feSAlex Zinenko 
97927c6d55cSMaksim Levental /// Emits builder that extracts results from op
98027c6d55cSMaksim Levental static void emitValueBuilder(const Operator &op,
981bccd37f6SRahul Joshi                              SmallVector<std::string> functionArgs,
98227c6d55cSMaksim Levental                              raw_ostream &os) {
98327c6d55cSMaksim Levental   // Params with (possibly) default args.
98427c6d55cSMaksim Levental   auto valueBuilderParams =
98527c6d55cSMaksim Levental       llvm::map_range(functionArgs, [](const std::string &argAndMaybeDefault) {
986bccd37f6SRahul Joshi         SmallVector<StringRef> argMaybeDefault =
98727c6d55cSMaksim Levental             llvm::to_vector<2>(llvm::split(argAndMaybeDefault, "="));
98827c6d55cSMaksim Levental         auto arg = llvm::convertToSnakeFromCamelCase(argMaybeDefault[0]);
98927c6d55cSMaksim Levental         if (argMaybeDefault.size() == 2)
99027c6d55cSMaksim Levental           return arg + "=" + argMaybeDefault[1].str();
99127c6d55cSMaksim Levental         return arg;
99227c6d55cSMaksim Levental       });
99327c6d55cSMaksim Levental   // Actual args passed to op builder (e.g., opParam=op_param).
99427c6d55cSMaksim Levental   auto opBuilderArgs = llvm::map_range(
99527c6d55cSMaksim Levental       llvm::make_filter_range(functionArgs,
99627c6d55cSMaksim Levental                               [](const std::string &s) { return s != "*"; }),
99727c6d55cSMaksim Levental       [](const std::string &arg) {
99827c6d55cSMaksim Levental         auto lhs = *llvm::split(arg, "=").begin();
99927c6d55cSMaksim Levental         return (lhs + "=" + llvm::convertToSnakeFromCamelCase(lhs)).str();
100027c6d55cSMaksim Levental       });
100151a4f319SPeter Hawkins   std::string nameWithoutDialect = sanitizeName(
100251a4f319SPeter Hawkins       op.getOperationName().substr(op.getOperationName().find('.') + 1));
100351a4f319SPeter Hawkins   std::string params = llvm::join(valueBuilderParams, ", ");
100451a4f319SPeter Hawkins   std::string args = llvm::join(opBuilderArgs, ", ");
100551a4f319SPeter Hawkins   const char *type =
100627c6d55cSMaksim Levental       (op.getNumResults() > 1
10076ce51599SSergei Lebedev            ? "_Sequence[_ods_ir.Value]"
100851a4f319SPeter Hawkins            : (op.getNumResults() > 0 ? "_ods_ir.Value" : "_ods_ir.Operation"));
100951a4f319SPeter Hawkins   if (op.getNumVariableLengthResults() > 0) {
101051a4f319SPeter Hawkins     os << formatv(valueBuilderVariadicTemplate, nameWithoutDialect,
101151a4f319SPeter Hawkins                   op.getCppClassName(), params, args, type);
101251a4f319SPeter Hawkins   } else {
101351a4f319SPeter Hawkins     const char *results;
101451a4f319SPeter Hawkins     if (op.getNumResults() == 0) {
101551a4f319SPeter Hawkins       results = "";
101651a4f319SPeter Hawkins     } else if (op.getNumResults() == 1) {
101751a4f319SPeter Hawkins       results = ".result";
101851a4f319SPeter Hawkins     } else {
101951a4f319SPeter Hawkins       results = ".results";
102051a4f319SPeter Hawkins     }
102151a4f319SPeter Hawkins     os << formatv(valueBuilderTemplate, nameWithoutDialect,
102251a4f319SPeter Hawkins                   op.getCppClassName(), params, args, type, results);
102351a4f319SPeter Hawkins   }
102427c6d55cSMaksim Levental }
102527c6d55cSMaksim Levental 
1026fd407e1fSAlex Zinenko /// Emits bindings for a specific Op to the given output stream.
102767a910bbSRahul Kayaith static void emitOpBindings(const Operator &op, raw_ostream &os) {
1028bccd37f6SRahul Joshi   os << formatv(opClassTemplate, op.getCppClassName(), op.getOperationName());
102971b6b010SStella Laurenzo 
103071b6b010SStella Laurenzo   // Sized segments.
103171b6b010SStella Laurenzo   if (op.getTrait(attrSizedTraitForKind("operand")) != nullptr) {
103271b6b010SStella Laurenzo     emitSegmentSpec(op, "OPERAND", getNumOperands, getOperand, os);
103371b6b010SStella Laurenzo   }
103471b6b010SStella Laurenzo   if (op.getTrait(attrSizedTraitForKind("result")) != nullptr) {
103571b6b010SStella Laurenzo     emitSegmentSpec(op, "RESULT", getNumResults, getResult, os);
103671b6b010SStella Laurenzo   }
103771b6b010SStella Laurenzo 
103871b6b010SStella Laurenzo   emitRegionAttributes(op, os);
1039bccd37f6SRahul Joshi   SmallVector<std::string> functionArgs = emitDefaultOpBuilder(op, os);
1040fd407e1fSAlex Zinenko   emitOperandAccessors(op, os);
104167a910bbSRahul Kayaith   emitAttributeAccessors(op, os);
1042fd407e1fSAlex Zinenko   emitResultAccessors(op, os);
104318fbd5feSAlex Zinenko   emitRegionAccessors(op, os);
104427c6d55cSMaksim Levental   emitValueBuilder(op, functionArgs, os);
1045fd407e1fSAlex Zinenko }
1046fd407e1fSAlex Zinenko 
1047fd407e1fSAlex Zinenko /// Emits bindings for the dialect specified in the command line, including file
1048fd407e1fSAlex Zinenko /// headers and utilities. Returns `false` on success to comply with Tablegen
1049fd407e1fSAlex Zinenko /// registration requirements.
1050bccd37f6SRahul Joshi static bool emitAllOps(const RecordKeeper &records, raw_ostream &os) {
1051fd407e1fSAlex Zinenko   if (clDialectName.empty())
1052fd407e1fSAlex Zinenko     llvm::PrintFatalError("dialect name not provided");
1053fd407e1fSAlex Zinenko 
1054a2288a89SMaksim Levental   os << fileHeader;
1055a2288a89SMaksim Levental   if (!clDialectExtensionName.empty())
1056bccd37f6SRahul Joshi     os << formatv(dialectExtensionTemplate, clDialectName.getValue());
10573f71765aSAlex Zinenko   else
1058bccd37f6SRahul Joshi     os << formatv(dialectClassTemplate, clDialectName.getValue());
1059922b26cdSMehdi Amini 
1060bccd37f6SRahul Joshi   for (const Record *rec : records.getAllDerivedDefinitions("Op")) {
1061fd407e1fSAlex Zinenko     Operator op(rec);
1062fd407e1fSAlex Zinenko     if (op.getDialectName() == clDialectName.getValue())
106367a910bbSRahul Kayaith       emitOpBindings(op, os);
1064fd407e1fSAlex Zinenko   }
1065fd407e1fSAlex Zinenko   return false;
1066fd407e1fSAlex Zinenko }
1067fd407e1fSAlex Zinenko 
1068fd407e1fSAlex Zinenko static GenRegistration
1069fd407e1fSAlex Zinenko     genPythonBindings("gen-python-op-bindings",
1070fd407e1fSAlex Zinenko                       "Generate Python bindings for MLIR Ops", &emitAllOps);
1071