1 //===- OpDefinitionsGen.cpp - MLIR op definitions generator ---------------===//
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 // OpDefinitionsGen uses the description of operations to generate C++
10 // definitions for ops.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "OpFormatGen.h"
15 #include "mlir/TableGen/Format.h"
16 #include "mlir/TableGen/GenInfo.h"
17 #include "mlir/TableGen/OpClass.h"
18 #include "mlir/TableGen/OpInterfaces.h"
19 #include "mlir/TableGen/OpTrait.h"
20 #include "mlir/TableGen/Operator.h"
21 #include "mlir/TableGen/SideEffects.h"
22 #include "llvm/ADT/Sequence.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/Support/Signals.h"
25 #include "llvm/TableGen/Error.h"
26 #include "llvm/TableGen/Record.h"
27 #include "llvm/TableGen/TableGenBackend.h"
28 
29 #define DEBUG_TYPE "mlir-tblgen-opdefgen"
30 
31 using namespace llvm;
32 using namespace mlir;
33 using namespace mlir::tblgen;
34 
35 static const char *const tblgenNamePrefix = "tblgen_";
36 static const char *const generatedArgName = "odsArg";
37 static const char *const builderOpState = "odsState";
38 
39 // The logic to calculate the actual value range for a declared operand/result
40 // of an op with variadic operands/results. Note that this logic is not for
41 // general use; it assumes all variadic operands/results must have the same
42 // number of values.
43 //
44 // {0}: The list of whether each declared operand/result is variadic.
45 // {1}: The total number of non-variadic operands/results.
46 // {2}: The total number of variadic operands/results.
47 // {3}: The total number of actual values.
48 // {4}: The begin iterator of the actual values.
49 // {5}: "operand" or "result".
50 const char *sameVariadicSizeValueRangeCalcCode = R"(
51   bool isVariadic[] = {{{0}};
52   int prevVariadicCount = 0;
53   for (unsigned i = 0; i < index; ++i)
54     if (isVariadic[i]) ++prevVariadicCount;
55 
56   // Calculate how many dynamic values a static variadic {5} corresponds to.
57   // This assumes all static variadic {5}s have the same dynamic value count.
58   int variadicSize = ({3} - {1}) / {2};
59   // `index` passed in as the parameter is the static index which counts each
60   // {5} (variadic or not) as size 1. So here for each previous static variadic
61   // {5}, we need to offset by (variadicSize - 1) to get where the dynamic
62   // value pack for this static {5} starts.
63   int offset = index + (variadicSize - 1) * prevVariadicCount;
64   int size = isVariadic[index] ? variadicSize : 1;
65 
66   return {{std::next({4}, offset), std::next({4}, offset + size)};
67 )";
68 
69 // The logic to calculate the actual value range for a declared operand/result
70 // of an op with variadic operands/results. Note that this logic is assumes
71 // the op has an attribute specifying the size of each operand/result segment
72 // (variadic or not).
73 //
74 // {0}: The name of the attribute specifying the segment sizes.
75 // {1}: The begin iterator of the actual values.
76 const char *attrSizedSegmentValueRangeCalcCode = R"(
77   auto sizeAttr = getAttrOfType<DenseIntElementsAttr>("{0}");
78   unsigned start = 0;
79   for (unsigned i = 0; i < index; ++i)
80     start += (*(sizeAttr.begin() + i)).getZExtValue();
81   unsigned end = start + (*(sizeAttr.begin() + index)).getZExtValue();
82   return {{std::next({1}, start), std::next({1}, end)};
83 )";
84 
85 static const char *const opCommentHeader = R"(
86 //===----------------------------------------------------------------------===//
87 // {0} {1}
88 //===----------------------------------------------------------------------===//
89 
90 )";
91 
92 //===----------------------------------------------------------------------===//
93 // Utility structs and functions
94 //===----------------------------------------------------------------------===//
95 
96 // Replaces all occurrences of `match` in `str` with `substitute`.
97 static std::string replaceAllSubstrs(std::string str, const std::string &match,
98                                      const std::string &substitute) {
99   std::string::size_type scanLoc = 0, matchLoc = std::string::npos;
100   while ((matchLoc = str.find(match, scanLoc)) != std::string::npos) {
101     str = str.replace(matchLoc, match.size(), substitute);
102     scanLoc = matchLoc + substitute.size();
103   }
104   return str;
105 }
106 
107 // Returns whether the record has a value of the given name that can be returned
108 // via getValueAsString.
109 static inline bool hasStringAttribute(const Record &record,
110                                       StringRef fieldName) {
111   auto valueInit = record.getValueInit(fieldName);
112   return isa<CodeInit>(valueInit) || isa<StringInit>(valueInit);
113 }
114 
115 static std::string getArgumentName(const Operator &op, int index) {
116   const auto &operand = op.getOperand(index);
117   if (!operand.name.empty())
118     return std::string(operand.name);
119   else
120     return std::string(formatv("{0}_{1}", generatedArgName, index));
121 }
122 
123 // Returns true if we can use unwrapped value for the given `attr` in builders.
124 static bool canUseUnwrappedRawValue(const tblgen::Attribute &attr) {
125   return attr.getReturnType() != attr.getStorageType() &&
126          // We need to wrap the raw value into an attribute in the builder impl
127          // so we need to make sure that the attribute specifies how to do that.
128          !attr.getConstBuilderTemplate().empty();
129 }
130 
131 //===----------------------------------------------------------------------===//
132 // Op emitter
133 //===----------------------------------------------------------------------===//
134 
135 namespace {
136 // Simple RAII helper for defining ifdef-undef-endif scopes.
137 class IfDefScope {
138 public:
139   IfDefScope(StringRef name, raw_ostream &os) : name(name), os(os) {
140     os << "#ifdef " << name << "\n"
141        << "#undef " << name << "\n\n";
142   }
143 
144   ~IfDefScope() { os << "\n#endif  // " << name << "\n\n"; }
145 
146 private:
147   StringRef name;
148   raw_ostream &os;
149 };
150 } // end anonymous namespace
151 
152 namespace {
153 // Helper class to emit a record into the given output stream.
154 class OpEmitter {
155 public:
156   static void emitDecl(const Operator &op, raw_ostream &os);
157   static void emitDef(const Operator &op, raw_ostream &os);
158 
159 private:
160   OpEmitter(const Operator &op);
161 
162   void emitDecl(raw_ostream &os);
163   void emitDef(raw_ostream &os);
164 
165   // Generates the OpAsmOpInterface for this operation if possible.
166   void genOpAsmInterface();
167 
168   // Generates the `getOperationName` method for this op.
169   void genOpNameGetter();
170 
171   // Generates getters for the attributes.
172   void genAttrGetters();
173 
174   // Generates setter for the attributes.
175   void genAttrSetters();
176 
177   // Generates getters for named operands.
178   void genNamedOperandGetters();
179 
180   // Generates getters for named results.
181   void genNamedResultGetters();
182 
183   // Generates getters for named regions.
184   void genNamedRegionGetters();
185 
186   // Generates getters for named successors.
187   void genNamedSuccessorGetters();
188 
189   // Generates builder methods for the operation.
190   void genBuilder();
191 
192   // Generates the build() method that takes each operand/attribute
193   // as a stand-alone parameter.
194   void genSeparateArgParamBuilder();
195 
196   // Generates the build() method that takes each operand/attribute as a
197   // stand-alone parameter. The generated build() method uses first operand's
198   // type as all results' types.
199   void genUseOperandAsResultTypeSeparateParamBuilder();
200 
201   // Generates the build() method that takes all operands/attributes
202   // collectively as one parameter. The generated build() method uses first
203   // operand's type as all results' types.
204   void genUseOperandAsResultTypeCollectiveParamBuilder();
205 
206   // Generates the build() method that takes aggregate operands/attributes
207   // parameters. This build() method uses inferred types as result types.
208   // Requires: The type needs to be inferable via InferTypeOpInterface.
209   void genInferredTypeCollectiveParamBuilder();
210 
211   // Generates the build() method that takes each operand/attribute as a
212   // stand-alone parameter. The generated build() method uses first attribute's
213   // type as all result's types.
214   void genUseAttrAsResultTypeBuilder();
215 
216   // Generates the build() method that takes all result types collectively as
217   // one parameter. Similarly for operands and attributes.
218   void genCollectiveParamBuilder();
219 
220   // The kind of parameter to generate for result types in builders.
221   enum class TypeParamKind {
222     None,       // No result type in parameter list.
223     Separate,   // A separate parameter for each result type.
224     Collective, // An ArrayRef<Type> for all result types.
225   };
226 
227   // The kind of parameter to generate for attributes in builders.
228   enum class AttrParamKind {
229     WrappedAttr,    // A wrapped MLIR Attribute instance.
230     UnwrappedValue, // A raw value without MLIR Attribute wrapper.
231   };
232 
233   // Builds the parameter list for build() method of this op. This method writes
234   // to `paramList` the comma-separated parameter list and updates
235   // `resultTypeNames` with the names for parameters for specifying result
236   // types. The given `typeParamKind` and `attrParamKind` controls how result
237   // types and attributes are placed in the parameter list.
238   void buildParamList(std::string &paramList,
239                       SmallVectorImpl<std::string> &resultTypeNames,
240                       TypeParamKind typeParamKind,
241                       AttrParamKind attrParamKind = AttrParamKind::WrappedAttr);
242 
243   // Adds op arguments and regions into operation state for build() methods.
244   void genCodeForAddingArgAndRegionForBuilder(OpMethodBody &body,
245                                               bool isRawValueAttr = false);
246 
247   // Generates canonicalizer declaration for the operation.
248   void genCanonicalizerDecls();
249 
250   // Generates the folder declaration for the operation.
251   void genFolderDecls();
252 
253   // Generates the parser for the operation.
254   void genParser();
255 
256   // Generates the printer for the operation.
257   void genPrinter();
258 
259   // Generates verify method for the operation.
260   void genVerifier();
261 
262   // Generates verify statements for operands and results in the operation.
263   // The generated code will be attached to `body`.
264   void genOperandResultVerifier(OpMethodBody &body,
265                                 Operator::value_range values,
266                                 StringRef valueKind);
267 
268   // Generates verify statements for regions in the operation.
269   // The generated code will be attached to `body`.
270   void genRegionVerifier(OpMethodBody &body);
271 
272   // Generates verify statements for successors in the operation.
273   // The generated code will be attached to `body`.
274   void genSuccessorVerifier(OpMethodBody &body);
275 
276   // Generates the traits used by the object.
277   void genTraits();
278 
279   // Generate the OpInterface methods.
280   void genOpInterfaceMethods();
281 
282   // Generate the side effect interface methods.
283   void genSideEffectInterfaceMethods();
284 
285 private:
286   // The TableGen record for this op.
287   // TODO(antiagainst,zinenko): OpEmitter should not have a Record directly,
288   // it should rather go through the Operator for better abstraction.
289   const Record &def;
290 
291   // The wrapper operator class for querying information from this op.
292   Operator op;
293 
294   // The C++ code builder for this op
295   OpClass opClass;
296 
297   // The format context for verification code generation.
298   FmtContext verifyCtx;
299 };
300 } // end anonymous namespace
301 
302 OpEmitter::OpEmitter(const Operator &op)
303     : def(op.getDef()), op(op),
304       opClass(op.getCppClassName(), op.getExtraClassDeclaration()) {
305   verifyCtx.withOp("(*this->getOperation())");
306 
307   genTraits();
308   // Generate C++ code for various op methods. The order here determines the
309   // methods in the generated file.
310   genOpAsmInterface();
311   genOpNameGetter();
312   genNamedOperandGetters();
313   genNamedResultGetters();
314   genNamedRegionGetters();
315   genNamedSuccessorGetters();
316   genAttrGetters();
317   genAttrSetters();
318   genBuilder();
319   genParser();
320   genPrinter();
321   genVerifier();
322   genCanonicalizerDecls();
323   genFolderDecls();
324   genOpInterfaceMethods();
325   generateOpFormat(op, opClass);
326   genSideEffectInterfaceMethods();
327 }
328 
329 void OpEmitter::emitDecl(const Operator &op, raw_ostream &os) {
330   OpEmitter(op).emitDecl(os);
331 }
332 
333 void OpEmitter::emitDef(const Operator &op, raw_ostream &os) {
334   OpEmitter(op).emitDef(os);
335 }
336 
337 void OpEmitter::emitDecl(raw_ostream &os) { opClass.writeDeclTo(os); }
338 
339 void OpEmitter::emitDef(raw_ostream &os) { opClass.writeDefTo(os); }
340 
341 void OpEmitter::genAttrGetters() {
342   FmtContext fctx;
343   fctx.withBuilder("mlir::Builder(this->getContext())");
344 
345   // Emit the derived attribute body.
346   auto emitDerivedAttr = [&](StringRef name, Attribute attr) {
347     auto &method = opClass.newMethod(attr.getReturnType(), name);
348     auto &body = method.body();
349     body << "  " << attr.getDerivedCodeBody() << "\n";
350   };
351 
352   // Emit with return type specified.
353   auto emitAttrWithReturnType = [&](StringRef name, Attribute attr) {
354     auto &method = opClass.newMethod(attr.getReturnType(), name);
355     auto &body = method.body();
356     body << "  auto attr = " << name << "Attr();\n";
357     if (attr.hasDefaultValue()) {
358       // Returns the default value if not set.
359       // TODO: this is inefficient, we are recreating the attribute for every
360       // call. This should be set instead.
361       std::string defaultValue = std::string(
362           tgfmt(attr.getConstBuilderTemplate(), &fctx, attr.getDefaultValue()));
363       body << "    if (!attr)\n      return "
364            << tgfmt(attr.getConvertFromStorageCall(),
365                     &fctx.withSelf(defaultValue))
366            << ";\n";
367     }
368     body << "  return "
369          << tgfmt(attr.getConvertFromStorageCall(), &fctx.withSelf("attr"))
370          << ";\n";
371   };
372 
373   // Generate raw named accessor type. This is a wrapper class that allows
374   // referring to the attributes via accessors instead of having to use
375   // the string interface for better compile time verification.
376   auto emitAttrWithStorageType = [&](StringRef name, Attribute attr) {
377     auto &method =
378         opClass.newMethod(attr.getStorageType(), (name + "Attr").str());
379     auto &body = method.body();
380     body << "  return this->getAttr(\"" << name << "\").";
381     if (attr.isOptional() || attr.hasDefaultValue())
382       body << "dyn_cast_or_null<";
383     else
384       body << "cast<";
385     body << attr.getStorageType() << ">();";
386   };
387 
388   for (auto &namedAttr : op.getAttributes()) {
389     const auto &name = namedAttr.name;
390     const auto &attr = namedAttr.attr;
391     if (attr.isDerivedAttr()) {
392       emitDerivedAttr(name, attr);
393     } else {
394       emitAttrWithStorageType(name, attr);
395       emitAttrWithReturnType(name, attr);
396     }
397   }
398 
399   // Generate helper method to query whether a named attribute is a derived
400   // attribute. This enables, for example, avoiding adding an attribute that
401   // overlaps with a derived attribute.
402   auto derivedAttr = make_filter_range(op.getAttributes(),
403                                        [](const NamedAttribute &namedAttr) {
404                                          return namedAttr.attr.isDerivedAttr();
405                                        });
406   if (!derivedAttr.empty()) {
407     opClass.addTrait("DerivedAttributeOpInterface::Trait");
408     auto &method = opClass.newMethod("bool", "isDerivedAttribute",
409                                      "StringRef name", OpMethod::MP_Static);
410     auto &body = method.body();
411     for (auto namedAttr : derivedAttr)
412       body << "    if (name == \"" << namedAttr.name << "\") return true;\n";
413     body << " return false;";
414   }
415 }
416 
417 void OpEmitter::genAttrSetters() {
418   // Generate raw named setter type. This is a wrapper class that allows setting
419   // to the attributes via setters instead of having to use the string interface
420   // for better compile time verification.
421   auto emitAttrWithStorageType = [&](StringRef name, Attribute attr) {
422     auto &method = opClass.newMethod("void", (name + "Attr").str(),
423                                      (attr.getStorageType() + " attr").str());
424     auto &body = method.body();
425     body << "  this->getOperation()->setAttr(\"" << name << "\", attr);";
426   };
427 
428   for (auto &namedAttr : op.getAttributes()) {
429     const auto &name = namedAttr.name;
430     const auto &attr = namedAttr.attr;
431     if (!attr.isDerivedAttr())
432       emitAttrWithStorageType(name, attr);
433   }
434 }
435 
436 // Generates the named operand getter methods for the given Operator `op` and
437 // puts them in `opClass`.  Uses `rangeType` as the return type of getters that
438 // return a range of operands (individual operands are `Value ` and each
439 // element in the range must also be `Value `); use `rangeBeginCall` to get
440 // an iterator to the beginning of the operand range; use `rangeSizeCall` to
441 // obtain the number of operands. `getOperandCallPattern` contains the code
442 // necessary to obtain a single operand whose position will be substituted
443 // instead of
444 // "{0}" marker in the pattern.  Note that the pattern should work for any kind
445 // of ops, in particular for one-operand ops that may not have the
446 // `getOperand(unsigned)` method.
447 static void generateNamedOperandGetters(const Operator &op, Class &opClass,
448                                         StringRef rangeType,
449                                         StringRef rangeBeginCall,
450                                         StringRef rangeSizeCall,
451                                         StringRef getOperandCallPattern) {
452   const int numOperands = op.getNumOperands();
453   const int numVariadicOperands = op.getNumVariableLengthOperands();
454   const int numNormalOperands = numOperands - numVariadicOperands;
455 
456   const auto *sameVariadicSize =
457       op.getTrait("OpTrait::SameVariadicOperandSize");
458   const auto *attrSizedOperands =
459       op.getTrait("OpTrait::AttrSizedOperandSegments");
460 
461   if (numVariadicOperands > 1 && !sameVariadicSize && !attrSizedOperands) {
462     PrintFatalError(op.getLoc(), "op has multiple variadic operands but no "
463                                  "specification over their sizes");
464   }
465 
466   if (numVariadicOperands < 2 && attrSizedOperands) {
467     PrintFatalError(op.getLoc(), "op must have at least two variadic operands "
468                                  "to use 'AttrSizedOperandSegments' trait");
469   }
470 
471   if (attrSizedOperands && sameVariadicSize) {
472     PrintFatalError(op.getLoc(),
473                     "op cannot have both 'AttrSizedOperandSegments' and "
474                     "'SameVariadicOperandSize' traits");
475   }
476 
477   // First emit a "sink" getter method upon which we layer all nicer named
478   // getter methods.
479   auto &m = opClass.newMethod(rangeType, "getODSOperands", "unsigned index");
480 
481   if (numVariadicOperands == 0) {
482     // We still need to match the return type, which is a range.
483     m.body() << "  return {std::next(" << rangeBeginCall
484              << ", index), std::next(" << rangeBeginCall << ", index + 1)};";
485   } else if (attrSizedOperands) {
486     m.body() << formatv(attrSizedSegmentValueRangeCalcCode,
487                         "operand_segment_sizes", rangeBeginCall);
488   } else {
489     // Because the op can have arbitrarily interleaved variadic and non-variadic
490     // operands, we need to embed a list in the "sink" getter method for
491     // calculation at run-time.
492     llvm::SmallVector<StringRef, 4> isVariadic;
493     isVariadic.reserve(numOperands);
494     for (int i = 0; i < numOperands; ++i)
495       isVariadic.push_back(op.getOperand(i).isVariableLength() ? "true"
496                                                                : "false");
497     std::string isVariadicList = llvm::join(isVariadic, ", ");
498 
499     m.body() << formatv(sameVariadicSizeValueRangeCalcCode, isVariadicList,
500                         numNormalOperands, numVariadicOperands, rangeSizeCall,
501                         rangeBeginCall, "operand");
502   }
503 
504   // Then we emit nicer named getter methods by redirecting to the "sink" getter
505   // method.
506 
507   for (int i = 0; i != numOperands; ++i) {
508     const auto &operand = op.getOperand(i);
509     if (operand.name.empty())
510       continue;
511 
512     if (operand.isOptional()) {
513       auto &m = opClass.newMethod("Value", operand.name);
514       m.body() << "  auto operands = getODSOperands(" << i << ");\n"
515                << "  return operands.empty() ? Value() : *operands.begin();";
516     } else if (operand.isVariadic()) {
517       auto &m = opClass.newMethod(rangeType, operand.name);
518       m.body() << "  return getODSOperands(" << i << ");";
519     } else {
520       auto &m = opClass.newMethod("Value", operand.name);
521       m.body() << "  return *getODSOperands(" << i << ").begin();";
522     }
523   }
524 }
525 
526 void OpEmitter::genNamedOperandGetters() {
527   if (op.getTrait("OpTrait::AttrSizedOperandSegments"))
528     opClass.setHasOperandAdaptorClass(false);
529 
530   generateNamedOperandGetters(
531       op, opClass, /*rangeType=*/"Operation::operand_range",
532       /*rangeBeginCall=*/"getOperation()->operand_begin()",
533       /*rangeSizeCall=*/"getOperation()->getNumOperands()",
534       /*getOperandCallPattern=*/"getOperation()->getOperand({0})");
535 }
536 
537 void OpEmitter::genNamedResultGetters() {
538   const int numResults = op.getNumResults();
539   const int numVariadicResults = op.getNumVariableLengthResults();
540   const int numNormalResults = numResults - numVariadicResults;
541 
542   // If we have more than one variadic results, we need more complicated logic
543   // to calculate the value range for each result.
544 
545   const auto *sameVariadicSize = op.getTrait("OpTrait::SameVariadicResultSize");
546   const auto *attrSizedResults =
547       op.getTrait("OpTrait::AttrSizedResultSegments");
548 
549   if (numVariadicResults > 1 && !sameVariadicSize && !attrSizedResults) {
550     PrintFatalError(op.getLoc(), "op has multiple variadic results but no "
551                                  "specification over their sizes");
552   }
553 
554   if (numVariadicResults < 2 && attrSizedResults) {
555     PrintFatalError(op.getLoc(), "op must have at least two variadic results "
556                                  "to use 'AttrSizedResultSegments' trait");
557   }
558 
559   if (attrSizedResults && sameVariadicSize) {
560     PrintFatalError(op.getLoc(),
561                     "op cannot have both 'AttrSizedResultSegments' and "
562                     "'SameVariadicResultSize' traits");
563   }
564 
565   auto &m = opClass.newMethod("Operation::result_range", "getODSResults",
566                               "unsigned index");
567 
568   if (numVariadicResults == 0) {
569     m.body() << "  return {std::next(getOperation()->result_begin(), index), "
570                 "std::next(getOperation()->result_begin(), index + 1)};";
571   } else if (attrSizedResults) {
572     m.body() << formatv(attrSizedSegmentValueRangeCalcCode,
573                         "result_segment_sizes",
574                         "getOperation()->result_begin()");
575   } else {
576     llvm::SmallVector<StringRef, 4> isVariadic;
577     isVariadic.reserve(numResults);
578     for (int i = 0; i < numResults; ++i)
579       isVariadic.push_back(op.getResult(i).isVariableLength() ? "true"
580                                                               : "false");
581     std::string isVariadicList = llvm::join(isVariadic, ", ");
582 
583     m.body() << formatv(sameVariadicSizeValueRangeCalcCode, isVariadicList,
584                         numNormalResults, numVariadicResults,
585                         "getOperation()->getNumResults()",
586                         "getOperation()->result_begin()", "result");
587   }
588 
589   for (int i = 0; i != numResults; ++i) {
590     const auto &result = op.getResult(i);
591     if (result.name.empty())
592       continue;
593 
594     if (result.isOptional()) {
595       auto &m = opClass.newMethod("Value", result.name);
596       m.body() << "  auto results = getODSResults(" << i << ");\n"
597                << "  return results.empty() ? Value() : *results.begin();";
598     } else if (result.isVariadic()) {
599       auto &m = opClass.newMethod("Operation::result_range", result.name);
600       m.body() << "  return getODSResults(" << i << ");";
601     } else {
602       auto &m = opClass.newMethod("Value", result.name);
603       m.body() << "  return *getODSResults(" << i << ").begin();";
604     }
605   }
606 }
607 
608 void OpEmitter::genNamedRegionGetters() {
609   unsigned numRegions = op.getNumRegions();
610   for (unsigned i = 0; i < numRegions; ++i) {
611     const auto &region = op.getRegion(i);
612     if (region.name.empty())
613       continue;
614 
615     // Generate the accessors for a varidiadic region.
616     if (region.isVariadic()) {
617       auto &m = opClass.newMethod("MutableArrayRef<Region>", region.name);
618       m.body() << formatv(
619           "  return this->getOperation()->getRegions().drop_front({0});", i);
620       continue;
621     }
622 
623     auto &m = opClass.newMethod("Region &", region.name);
624     m.body() << formatv("  return this->getOperation()->getRegion({0});", i);
625   }
626 }
627 
628 void OpEmitter::genNamedSuccessorGetters() {
629   unsigned numSuccessors = op.getNumSuccessors();
630   for (unsigned i = 0; i < numSuccessors; ++i) {
631     const NamedSuccessor &successor = op.getSuccessor(i);
632     if (successor.name.empty())
633       continue;
634 
635     // Generate the accessors for a variadic successor list.
636     if (successor.isVariadic()) {
637       auto &m = opClass.newMethod("SuccessorRange", successor.name);
638       m.body() << formatv(
639           "  return {std::next(this->getOperation()->successor_begin(), {0}), "
640           "this->getOperation()->successor_end()};",
641           i);
642       continue;
643     }
644 
645     auto &m = opClass.newMethod("Block *", successor.name);
646     m.body() << formatv("  return this->getOperation()->getSuccessor({0});", i);
647   }
648 }
649 
650 static bool canGenerateUnwrappedBuilder(Operator &op) {
651   // If this op does not have native attributes at all, return directly to avoid
652   // redefining builders.
653   if (op.getNumNativeAttributes() == 0)
654     return false;
655 
656   bool canGenerate = false;
657   // We are generating builders that take raw values for attributes. We need to
658   // make sure the native attributes have a meaningful "unwrapped" value type
659   // different from the wrapped mlir::Attribute type to avoid redefining
660   // builders. This checks for the op has at least one such native attribute.
661   for (int i = 0, e = op.getNumNativeAttributes(); i < e; ++i) {
662     NamedAttribute &namedAttr = op.getAttribute(i);
663     if (canUseUnwrappedRawValue(namedAttr.attr)) {
664       canGenerate = true;
665       break;
666     }
667   }
668   return canGenerate;
669 }
670 
671 void OpEmitter::genSeparateArgParamBuilder() {
672   SmallVector<AttrParamKind, 2> attrBuilderType;
673   attrBuilderType.push_back(AttrParamKind::WrappedAttr);
674   if (canGenerateUnwrappedBuilder(op))
675     attrBuilderType.push_back(AttrParamKind::UnwrappedValue);
676 
677   // Emit with separate builders with or without unwrapped attributes and/or
678   // inferring result type.
679   auto emit = [&](AttrParamKind attrType, TypeParamKind paramKind,
680                   bool inferType) {
681     std::string paramList;
682     llvm::SmallVector<std::string, 4> resultNames;
683     buildParamList(paramList, resultNames, paramKind, attrType);
684 
685     auto &m =
686         opClass.newMethod("void", "build", paramList, OpMethod::MP_Static);
687     auto &body = m.body();
688     genCodeForAddingArgAndRegionForBuilder(
689         body, /*isRawValueAttr=*/attrType == AttrParamKind::UnwrappedValue);
690 
691     // Push all result types to the operation state
692 
693     if (inferType) {
694       // Generate builder that infers type too.
695       // TODO(jpienaar): Subsume this with general checking if type can be
696       // inferred automatically.
697       // TODO(jpienaar): Expand to handle regions.
698       body << formatv(R"(
699         SmallVector<Type, 2> inferredReturnTypes;
700         if (succeeded({0}::inferReturnTypes(odsBuilder->getContext(),
701                       {1}.location, {1}.operands, {1}.attributes,
702                       /*regions=*/{{}, inferredReturnTypes)))
703           {1}.addTypes(inferredReturnTypes);
704         else
705           llvm::report_fatal_error("Failed to infer result type(s).");)",
706                       opClass.getClassName(), builderOpState);
707       return;
708     }
709 
710     switch (paramKind) {
711     case TypeParamKind::None:
712       return;
713     case TypeParamKind::Separate:
714       for (int i = 0, e = op.getNumResults(); i < e; ++i) {
715         if (op.getResult(i).isOptional())
716           body << "  if (" << resultNames[i] << ")\n  ";
717         body << "  " << builderOpState << ".addTypes(" << resultNames[i]
718              << ");\n";
719       }
720       return;
721     case TypeParamKind::Collective:
722       body << "  "
723            << "assert(resultTypes.size() "
724            << (op.getNumVariableLengthResults() == 0 ? "==" : ">=") << " "
725            << (op.getNumResults() - op.getNumVariableLengthResults())
726            << "u && \"mismatched number of results\");\n";
727       body << "  " << builderOpState << ".addTypes(resultTypes);\n";
728       return;
729     }
730     llvm_unreachable("unhandled TypeParamKind");
731   };
732 
733   bool canInferType =
734       op.getTrait("InferTypeOpInterface::Trait") && op.getNumRegions() == 0;
735   for (auto attrType : attrBuilderType) {
736     emit(attrType, TypeParamKind::Separate, /*inferType=*/false);
737     if (canInferType)
738       emit(attrType, TypeParamKind::None, /*inferType=*/true);
739     // Emit separate arg build with collective type, unless there is only one
740     // variadic result, in which case the above would have already generated
741     // the same build method.
742     if (!(op.getNumResults() == 1 && op.getResult(0).isVariableLength()))
743       emit(attrType, TypeParamKind::Collective, /*inferType=*/false);
744   }
745 }
746 
747 void OpEmitter::genUseOperandAsResultTypeCollectiveParamBuilder() {
748   // If this op has a variadic result, we cannot generate this builder because
749   // we don't know how many results to create.
750   if (op.getNumVariableLengthResults() != 0)
751     return;
752 
753   int numResults = op.getNumResults();
754 
755   // Signature
756   std::string params =
757       std::string("Builder *odsBuilder, OperationState &") + builderOpState +
758       ", ValueRange operands, ArrayRef<NamedAttribute> attributes";
759   if (op.getNumVariadicRegions())
760     params += ", unsigned numRegions";
761   auto &m = opClass.newMethod("void", "build", params, OpMethod::MP_Static);
762   auto &body = m.body();
763 
764   // Operands
765   body << "  " << builderOpState << ".addOperands(operands);\n\n";
766 
767   // Attributes
768   body << "  " << builderOpState << ".addAttributes(attributes);\n";
769 
770   // Create the correct number of regions
771   if (int numRegions = op.getNumRegions()) {
772     body << llvm::formatv(
773         "  for (unsigned i = 0; i != {0}; ++i)\n",
774         (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));
775     body << "    (void)" << builderOpState << ".addRegion();\n";
776   }
777 
778   // Result types
779   SmallVector<std::string, 2> resultTypes(numResults, "operands[0].getType()");
780   body << "  " << builderOpState << ".addTypes({"
781        << llvm::join(resultTypes, ", ") << "});\n\n";
782 }
783 
784 void OpEmitter::genInferredTypeCollectiveParamBuilder() {
785   // TODO(jpienaar): Expand to support regions.
786   const char *params =
787       "Builder *odsBuilder, OperationState &{0}, "
788       "ValueRange operands, ArrayRef<NamedAttribute> attributes";
789   auto &m =
790       opClass.newMethod("void", "build", formatv(params, builderOpState).str(),
791                         OpMethod::MP_Static);
792   auto &body = m.body();
793   body << formatv(R"(
794     SmallVector<Type, 2> inferredReturnTypes;
795     if (succeeded({0}::inferReturnTypes(odsBuilder->getContext(),
796                   {1}.location, operands, attributes,
797                   /*regions=*/{{}, inferredReturnTypes)))
798       build(odsBuilder, odsState, inferredReturnTypes, operands, attributes);
799     else
800       llvm::report_fatal_error("Failed to infer result type(s).");)",
801                   opClass.getClassName(), builderOpState);
802 }
803 
804 void OpEmitter::genUseOperandAsResultTypeSeparateParamBuilder() {
805   std::string paramList;
806   llvm::SmallVector<std::string, 4> resultNames;
807   buildParamList(paramList, resultNames, TypeParamKind::None);
808 
809   auto &m = opClass.newMethod("void", "build", paramList, OpMethod::MP_Static);
810   genCodeForAddingArgAndRegionForBuilder(m.body());
811 
812   auto numResults = op.getNumResults();
813   if (numResults == 0)
814     return;
815 
816   // Push all result types to the operation state
817   const char *index = op.getOperand(0).isVariadic() ? ".front()" : "";
818   std::string resultType =
819       formatv("{0}{1}.getType()", getArgumentName(op, 0), index).str();
820   m.body() << "  " << builderOpState << ".addTypes({" << resultType;
821   for (int i = 1; i != numResults; ++i)
822     m.body() << ", " << resultType;
823   m.body() << "});\n\n";
824 }
825 
826 void OpEmitter::genUseAttrAsResultTypeBuilder() {
827   std::string params =
828       std::string("Builder *odsBuilder, OperationState &") + builderOpState +
829       ", ValueRange operands, ArrayRef<NamedAttribute> attributes";
830   auto &m = opClass.newMethod("void", "build", params, OpMethod::MP_Static);
831   auto &body = m.body();
832 
833   // Push all result types to the operation state
834   std::string resultType;
835   const auto &namedAttr = op.getAttribute(0);
836 
837   body << "  for (auto attr : attributes) {\n";
838   body << "    if (attr.first != \"" << namedAttr.name << "\") continue;\n";
839   if (namedAttr.attr.isTypeAttr()) {
840     resultType = "attr.second.cast<TypeAttr>().getValue()";
841   } else {
842     resultType = "attr.second.getType()";
843   }
844 
845   // Operands
846   body << "  " << builderOpState << ".addOperands(operands);\n\n";
847   // Attributes
848   body << "  " << builderOpState << ".addAttributes(attributes);\n";
849 
850   // Result types
851   SmallVector<std::string, 2> resultTypes(op.getNumResults(), resultType);
852   body << "    " << builderOpState << ".addTypes({"
853        << llvm::join(resultTypes, ", ") << "});\n";
854   body << "  }\n";
855 }
856 
857 void OpEmitter::genBuilder() {
858   // Handle custom builders if provided.
859   // TODO(antiagainst): Create wrapper class for OpBuilder to hide the native
860   // TableGen API calls here.
861   {
862     auto *listInit = dyn_cast_or_null<ListInit>(def.getValueInit("builders"));
863     if (listInit) {
864       for (Init *init : listInit->getValues()) {
865         Record *builderDef = cast<DefInit>(init)->getDef();
866         StringRef params = builderDef->getValueAsString("params");
867         StringRef body = builderDef->getValueAsString("body");
868         bool hasBody = !body.empty();
869 
870         auto &method =
871             opClass.newMethod("void", "build", params, OpMethod::MP_Static,
872                               /*declOnly=*/!hasBody);
873         if (hasBody)
874           method.body() << body;
875       }
876     }
877     if (op.skipDefaultBuilders()) {
878       if (!listInit || listInit->empty())
879         PrintFatalError(
880             op.getLoc(),
881             "default builders are skipped and no custom builders provided");
882       return;
883     }
884   }
885 
886   // Generate default builders that requires all result type, operands, and
887   // attributes as parameters.
888 
889   // We generate three classes of builders here:
890   // 1. one having a stand-alone parameter for each operand / attribute, and
891   genSeparateArgParamBuilder();
892   // 2. one having an aggregated parameter for all result types / operands /
893   //    attributes, and
894   genCollectiveParamBuilder();
895   // 3. one having a stand-alone parameter for each operand and attribute,
896   //    use the first operand or attribute's type as all result types
897   //    to facilitate different call patterns.
898   if (op.getNumVariableLengthResults() == 0) {
899     if (op.getTrait("OpTrait::SameOperandsAndResultType")) {
900       genUseOperandAsResultTypeSeparateParamBuilder();
901       genUseOperandAsResultTypeCollectiveParamBuilder();
902     }
903     if (op.getTrait("OpTrait::FirstAttrDerivedResultType"))
904       genUseAttrAsResultTypeBuilder();
905   }
906 }
907 
908 void OpEmitter::genCollectiveParamBuilder() {
909   int numResults = op.getNumResults();
910   int numVariadicResults = op.getNumVariableLengthResults();
911   int numNonVariadicResults = numResults - numVariadicResults;
912 
913   int numOperands = op.getNumOperands();
914   int numVariadicOperands = op.getNumVariableLengthOperands();
915   int numNonVariadicOperands = numOperands - numVariadicOperands;
916   // Signature
917   std::string params = std::string("Builder *, OperationState &") +
918                        builderOpState +
919                        ", ArrayRef<Type> resultTypes, ValueRange operands, "
920                        "ArrayRef<NamedAttribute> attributes";
921   if (op.getNumVariadicRegions())
922     params += ", unsigned numRegions";
923   auto &m = opClass.newMethod("void", "build", params, OpMethod::MP_Static);
924   auto &body = m.body();
925 
926   // Operands
927   if (numVariadicOperands == 0 || numNonVariadicOperands != 0)
928     body << "  assert(operands.size()"
929          << (numVariadicOperands != 0 ? " >= " : " == ")
930          << numNonVariadicOperands
931          << "u && \"mismatched number of parameters\");\n";
932   body << "  " << builderOpState << ".addOperands(operands);\n\n";
933 
934   // Attributes
935   body << "  " << builderOpState << ".addAttributes(attributes);\n";
936 
937   // Create the correct number of regions
938   if (int numRegions = op.getNumRegions()) {
939     body << llvm::formatv(
940         "  for (unsigned i = 0; i != {0}; ++i)\n",
941         (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));
942     body << "    (void)" << builderOpState << ".addRegion();\n";
943   }
944 
945   // Result types
946   if (numVariadicResults == 0 || numNonVariadicResults != 0)
947     body << "  assert(resultTypes.size()"
948          << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults
949          << "u && \"mismatched number of return types\");\n";
950   body << "  " << builderOpState << ".addTypes(resultTypes);\n";
951 
952   // Generate builder that infers type too.
953   // TODO(jpienaar): Subsume this with general checking if type can be inferred
954   // automatically.
955   // TODO(jpienaar): Expand to handle regions and successors.
956   if (op.getTrait("InferTypeOpInterface::Trait") && op.getNumRegions() == 0 &&
957       op.getNumSuccessors() == 0)
958     genInferredTypeCollectiveParamBuilder();
959 }
960 
961 void OpEmitter::buildParamList(std::string &paramList,
962                                SmallVectorImpl<std::string> &resultTypeNames,
963                                TypeParamKind typeParamKind,
964                                AttrParamKind attrParamKind) {
965   resultTypeNames.clear();
966   auto numResults = op.getNumResults();
967   resultTypeNames.reserve(numResults);
968 
969   paramList = "Builder *odsBuilder, OperationState &";
970   paramList.append(builderOpState);
971 
972   switch (typeParamKind) {
973   case TypeParamKind::None:
974     break;
975   case TypeParamKind::Separate: {
976     // Add parameters for all return types
977     for (int i = 0; i < numResults; ++i) {
978       const auto &result = op.getResult(i);
979       std::string resultName = std::string(result.name);
980       if (resultName.empty())
981         resultName = std::string(formatv("resultType{0}", i));
982 
983       if (result.isOptional())
984         paramList.append(", /*optional*/Type ");
985       else if (result.isVariadic())
986         paramList.append(", ArrayRef<Type> ");
987       else
988         paramList.append(", Type ");
989       paramList.append(resultName);
990 
991       resultTypeNames.emplace_back(std::move(resultName));
992     }
993   } break;
994   case TypeParamKind::Collective: {
995     paramList.append(", ArrayRef<Type> resultTypes");
996     resultTypeNames.push_back("resultTypes");
997   } break;
998   }
999 
1000   // Add parameters for all arguments (operands and attributes).
1001 
1002   int numOperands = 0;
1003   int numAttrs = 0;
1004 
1005   int defaultValuedAttrStartIndex = op.getNumArgs();
1006   if (attrParamKind == AttrParamKind::UnwrappedValue) {
1007     // Calculate the start index from which we can attach default values in the
1008     // builder declaration.
1009     for (int i = op.getNumArgs() - 1; i >= 0; --i) {
1010       auto *namedAttr = op.getArg(i).dyn_cast<tblgen::NamedAttribute *>();
1011       if (!namedAttr || !namedAttr->attr.hasDefaultValue())
1012         break;
1013 
1014       if (!canUseUnwrappedRawValue(namedAttr->attr))
1015         break;
1016 
1017       // Creating an APInt requires us to provide bitwidth, value, and
1018       // signedness, which is complicated compared to others. Similarly
1019       // for APFloat.
1020       // TODO(b/144412160) Adjust the 'returnType' field of such attributes
1021       // to support them.
1022       StringRef retType = namedAttr->attr.getReturnType();
1023       if (retType == "APInt" || retType == "APFloat")
1024         break;
1025 
1026       defaultValuedAttrStartIndex = i;
1027     }
1028   }
1029 
1030   for (int i = 0, e = op.getNumArgs(); i < e; ++i) {
1031     auto argument = op.getArg(i);
1032     if (argument.is<tblgen::NamedTypeConstraint *>()) {
1033       const auto &operand = op.getOperand(numOperands);
1034       if (operand.isOptional())
1035         paramList.append(", /*optional*/Value ");
1036       else if (operand.isVariadic())
1037         paramList.append(", ValueRange ");
1038       else
1039         paramList.append(", Value ");
1040       paramList.append(getArgumentName(op, numOperands));
1041       ++numOperands;
1042     } else {
1043       const auto &namedAttr = op.getAttribute(numAttrs);
1044       const auto &attr = namedAttr.attr;
1045       paramList.append(", ");
1046 
1047       if (attr.isOptional())
1048         paramList.append("/*optional*/");
1049 
1050       switch (attrParamKind) {
1051       case AttrParamKind::WrappedAttr:
1052         paramList.append(std::string(attr.getStorageType()));
1053         break;
1054       case AttrParamKind::UnwrappedValue:
1055         if (canUseUnwrappedRawValue(attr)) {
1056           paramList.append(std::string(attr.getReturnType()));
1057         } else {
1058           paramList.append(std::string(attr.getStorageType()));
1059         }
1060         break;
1061       }
1062       paramList.append(" ");
1063       paramList.append(std::string(namedAttr.name));
1064 
1065       // Attach default value if requested and possible.
1066       if (attrParamKind == AttrParamKind::UnwrappedValue &&
1067           i >= defaultValuedAttrStartIndex) {
1068         bool isString = attr.getReturnType() == "StringRef";
1069         paramList.append(" = ");
1070         if (isString)
1071           paramList.append("\"");
1072         paramList.append(std::string(attr.getDefaultValue()));
1073         if (isString)
1074           paramList.append("\"");
1075       }
1076       ++numAttrs;
1077     }
1078   }
1079 
1080   /// Insert parameters for each successor.
1081   for (const NamedSuccessor &succ : op.getSuccessors()) {
1082     paramList += (succ.isVariadic() ? ", ArrayRef<Block *> " : ", Block *");
1083     paramList += succ.name;
1084   }
1085 
1086   /// Insert parameters for variadic regions.
1087   for (const NamedRegion &region : op.getRegions()) {
1088     if (region.isVariadic())
1089       paramList += llvm::formatv(", unsigned {0}Count", region.name).str();
1090   }
1091 }
1092 
1093 void OpEmitter::genCodeForAddingArgAndRegionForBuilder(OpMethodBody &body,
1094                                                        bool isRawValueAttr) {
1095   // Push all operands to the result.
1096   for (int i = 0, e = op.getNumOperands(); i < e; ++i) {
1097     std::string argName = getArgumentName(op, i);
1098     if (op.getOperand(i).isOptional())
1099       body << "  if (" << argName << ")\n  ";
1100     body << "  " << builderOpState << ".addOperands(" << argName << ");\n";
1101   }
1102 
1103   // If the operation has the operand segment size attribute, add it here.
1104   if (op.getTrait("OpTrait::AttrSizedOperandSegments")) {
1105     body << "  " << builderOpState
1106          << ".addAttribute(\"operand_segment_sizes\", "
1107             "odsBuilder->getI32VectorAttr({";
1108     llvm::interleaveComma(
1109         llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {
1110           if (op.getOperand(i).isOptional())
1111             body << "(" << getArgumentName(op, i) << " ? 1 : 0)";
1112           else if (op.getOperand(i).isVariadic())
1113             body << "static_cast<int32_t>(" << getArgumentName(op, i)
1114                  << ".size())";
1115           else
1116             body << "1";
1117         });
1118     body << "}));\n";
1119   }
1120 
1121   // Push all attributes to the result.
1122   for (const auto &namedAttr : op.getAttributes()) {
1123     auto &attr = namedAttr.attr;
1124     if (!attr.isDerivedAttr()) {
1125       bool emitNotNullCheck = attr.isOptional();
1126       if (emitNotNullCheck) {
1127         body << formatv("  if ({0}) ", namedAttr.name) << "{\n";
1128       }
1129       if (isRawValueAttr && canUseUnwrappedRawValue(attr)) {
1130         // If this is a raw value, then we need to wrap it in an Attribute
1131         // instance.
1132         FmtContext fctx;
1133         fctx.withBuilder("(*odsBuilder)");
1134 
1135         std::string builderTemplate =
1136             std::string(attr.getConstBuilderTemplate());
1137 
1138         // For StringAttr, its constant builder call will wrap the input in
1139         // quotes, which is correct for normal string literals, but incorrect
1140         // here given we use function arguments. So we need to strip the
1141         // wrapping quotes.
1142         if (StringRef(builderTemplate).contains("\"$0\""))
1143           builderTemplate = replaceAllSubstrs(builderTemplate, "\"$0\"", "$0");
1144 
1145         std::string value =
1146             std::string(tgfmt(builderTemplate, &fctx, namedAttr.name));
1147         body << formatv("  {0}.addAttribute(\"{1}\", {2});\n", builderOpState,
1148                         namedAttr.name, value);
1149       } else {
1150         body << formatv("  {0}.addAttribute(\"{1}\", {1});\n", builderOpState,
1151                         namedAttr.name);
1152       }
1153       if (emitNotNullCheck) {
1154         body << "  }\n";
1155       }
1156     }
1157   }
1158 
1159   // Create the correct number of regions.
1160   for (const NamedRegion &region : op.getRegions()) {
1161     if (region.isVariadic())
1162       body << formatv("  for (unsigned i = 0; i < {0}Count; ++i)\n  ",
1163                       region.name);
1164 
1165     body << "  (void)" << builderOpState << ".addRegion();\n";
1166   }
1167 
1168   // Push all successors to the result.
1169   for (const NamedSuccessor &namedSuccessor : op.getSuccessors()) {
1170     body << formatv("  {0}.addSuccessors({1});\n", builderOpState,
1171                     namedSuccessor.name);
1172   }
1173 }
1174 
1175 void OpEmitter::genCanonicalizerDecls() {
1176   if (!def.getValueAsBit("hasCanonicalizer"))
1177     return;
1178 
1179   const char *const params =
1180       "OwningRewritePatternList &results, MLIRContext *context";
1181   opClass.newMethod("void", "getCanonicalizationPatterns", params,
1182                     OpMethod::MP_Static, /*declOnly=*/true);
1183 }
1184 
1185 void OpEmitter::genFolderDecls() {
1186   bool hasSingleResult =
1187       op.getNumResults() == 1 && op.getNumVariableLengthResults() == 0;
1188 
1189   if (def.getValueAsBit("hasFolder")) {
1190     if (hasSingleResult) {
1191       const char *const params = "ArrayRef<Attribute> operands";
1192       opClass.newMethod("OpFoldResult", "fold", params, OpMethod::MP_None,
1193                         /*declOnly=*/true);
1194     } else {
1195       const char *const params = "ArrayRef<Attribute> operands, "
1196                                  "SmallVectorImpl<OpFoldResult> &results";
1197       opClass.newMethod("LogicalResult", "fold", params, OpMethod::MP_None,
1198                         /*declOnly=*/true);
1199     }
1200   }
1201 }
1202 
1203 void OpEmitter::genOpInterfaceMethods() {
1204   for (const auto &trait : op.getTraits()) {
1205     auto opTrait = dyn_cast<tblgen::InterfaceOpTrait>(&trait);
1206     if (!opTrait || !opTrait->shouldDeclareMethods())
1207       continue;
1208     auto interface = opTrait->getOpInterface();
1209     for (auto method : interface.getMethods()) {
1210       // Don't declare if the method has a body or a default implementation.
1211       if (method.getBody() || method.getDefaultImplementation())
1212         continue;
1213       std::string args;
1214       llvm::raw_string_ostream os(args);
1215       llvm::interleaveComma(method.getArguments(), os,
1216                             [&](const OpInterfaceMethod::Argument &arg) {
1217                               os << arg.type << " " << arg.name;
1218                             });
1219       opClass.newMethod(method.getReturnType(), method.getName(), os.str(),
1220                         method.isStatic() ? OpMethod::MP_Static
1221                                           : OpMethod::MP_None,
1222                         /*declOnly=*/true);
1223     }
1224   }
1225 }
1226 
1227 void OpEmitter::genSideEffectInterfaceMethods() {
1228   enum EffectKind { Operand, Result, Static };
1229   struct EffectLocation {
1230     /// The effect applied.
1231     SideEffect effect;
1232 
1233     /// The index if the kind is either operand or result.
1234     unsigned index : 30;
1235 
1236     /// The kind of the location.
1237     unsigned kind : 2;
1238   };
1239 
1240   StringMap<SmallVector<EffectLocation, 1>> interfaceEffects;
1241   auto resolveDecorators = [&](Operator::var_decorator_range decorators,
1242                                unsigned index, unsigned kind) {
1243     for (auto decorator : decorators)
1244       if (SideEffect *effect = dyn_cast<SideEffect>(&decorator))
1245         interfaceEffects[effect->getBaseEffectName()].push_back(
1246             EffectLocation{*effect, index, kind});
1247   };
1248 
1249   // Collect effects that were specified via:
1250   /// Traits.
1251   for (const auto &trait : op.getTraits()) {
1252     const auto *opTrait = dyn_cast<tblgen::SideEffectTrait>(&trait);
1253     if (!opTrait)
1254       continue;
1255     auto &effects = interfaceEffects[opTrait->getBaseEffectName()];
1256     for (auto decorator : opTrait->getEffects())
1257       effects.push_back(EffectLocation{cast<SideEffect>(decorator),
1258                                        /*index=*/0, EffectKind::Static});
1259   }
1260   /// Operands.
1261   for (unsigned i = 0, operandIt = 0, e = op.getNumArgs(); i != e; ++i) {
1262     if (op.getArg(i).is<NamedTypeConstraint *>()) {
1263       resolveDecorators(op.getArgDecorators(i), operandIt, EffectKind::Operand);
1264       ++operandIt;
1265     }
1266   }
1267   /// Results.
1268   for (unsigned i = 0, e = op.getNumResults(); i != e; ++i)
1269     resolveDecorators(op.getResultDecorators(i), i, EffectKind::Result);
1270 
1271   for (auto &it : interfaceEffects) {
1272     auto effectsParam =
1273         llvm::formatv(
1274             "SmallVectorImpl<SideEffects::EffectInstance<{0}>> &effects",
1275             it.first())
1276             .str();
1277 
1278     // Generate the 'getEffects' method.
1279     auto &getEffects = opClass.newMethod("void", "getEffects", effectsParam);
1280     auto &body = getEffects.body();
1281 
1282     // Add effect instances for each of the locations marked on the operation.
1283     for (auto &location : it.second) {
1284       if (location.kind != EffectKind::Static) {
1285         body << "  for (Value value : getODS"
1286              << (location.kind == EffectKind::Operand ? "Operands" : "Results")
1287              << "(" << location.index << "))\n  ";
1288       }
1289 
1290       body << "  effects.emplace_back(" << location.effect.getName()
1291            << "::get()";
1292 
1293       // If the effect isn't static, it has a specific value attached to it.
1294       if (location.kind != EffectKind::Static)
1295         body << ", value";
1296       body << ", " << location.effect.getResource() << "::get());\n";
1297     }
1298   }
1299 }
1300 
1301 void OpEmitter::genParser() {
1302   if (!hasStringAttribute(def, "parser") ||
1303       hasStringAttribute(def, "assemblyFormat"))
1304     return;
1305 
1306   auto &method = opClass.newMethod(
1307       "ParseResult", "parse", "OpAsmParser &parser, OperationState &result",
1308       OpMethod::MP_Static);
1309   FmtContext fctx;
1310   fctx.addSubst("cppClass", opClass.getClassName());
1311   auto parser = def.getValueAsString("parser").ltrim().rtrim(" \t\v\f\r");
1312   method.body() << "  " << tgfmt(parser, &fctx);
1313 }
1314 
1315 void OpEmitter::genPrinter() {
1316   if (hasStringAttribute(def, "assemblyFormat"))
1317     return;
1318 
1319   auto valueInit = def.getValueInit("printer");
1320   CodeInit *codeInit = dyn_cast<CodeInit>(valueInit);
1321   if (!codeInit)
1322     return;
1323 
1324   auto &method = opClass.newMethod("void", "print", "OpAsmPrinter &p");
1325   FmtContext fctx;
1326   fctx.addSubst("cppClass", opClass.getClassName());
1327   auto printer = codeInit->getValue().ltrim().rtrim(" \t\v\f\r");
1328   method.body() << "  " << tgfmt(printer, &fctx);
1329 }
1330 
1331 void OpEmitter::genVerifier() {
1332   auto valueInit = def.getValueInit("verifier");
1333   CodeInit *codeInit = dyn_cast<CodeInit>(valueInit);
1334   bool hasCustomVerify = codeInit && !codeInit->getValue().empty();
1335 
1336   auto &method = opClass.newMethod("LogicalResult", "verify", /*params=*/"");
1337   auto &body = method.body();
1338 
1339   const char *checkAttrSizedValueSegmentsCode = R"(
1340   auto sizeAttr = getAttrOfType<DenseIntElementsAttr>("{0}");
1341   auto numElements = sizeAttr.getType().cast<ShapedType>().getNumElements();
1342   if (numElements != {1}) {{
1343     return emitOpError("'{0}' attribute for specifying {2} segments "
1344                        "must have {1} elements");
1345   }
1346   )";
1347 
1348   // Verify a few traits first so that we can use
1349   // getODSOperands()/getODSResults() in the rest of the verifier.
1350   for (auto &trait : op.getTraits()) {
1351     if (auto *t = dyn_cast<tblgen::NativeOpTrait>(&trait)) {
1352       if (t->getTrait() == "OpTrait::AttrSizedOperandSegments") {
1353         body << formatv(checkAttrSizedValueSegmentsCode,
1354                         "operand_segment_sizes", op.getNumOperands(),
1355                         "operand");
1356       } else if (t->getTrait() == "OpTrait::AttrSizedResultSegments") {
1357         body << formatv(checkAttrSizedValueSegmentsCode, "result_segment_sizes",
1358                         op.getNumResults(), "result");
1359       }
1360     }
1361   }
1362 
1363   // Populate substitutions for attributes and named operands and results.
1364   for (const auto &namedAttr : op.getAttributes())
1365     verifyCtx.addSubst(namedAttr.name,
1366                        formatv("this->getAttr(\"{0}\")", namedAttr.name));
1367   for (int i = 0, e = op.getNumOperands(); i < e; ++i) {
1368     auto &value = op.getOperand(i);
1369     if (value.name.empty())
1370       continue;
1371 
1372     if (value.isVariadic())
1373       verifyCtx.addSubst(value.name, formatv("this->getODSOperands({0})", i));
1374     else
1375       verifyCtx.addSubst(value.name,
1376                          formatv("(*this->getODSOperands({0}).begin())", i));
1377   }
1378   for (int i = 0, e = op.getNumResults(); i < e; ++i) {
1379     auto &value = op.getResult(i);
1380     if (value.name.empty())
1381       continue;
1382 
1383     if (value.isVariadic())
1384       verifyCtx.addSubst(value.name, formatv("this->getODSResults({0})", i));
1385     else
1386       verifyCtx.addSubst(value.name,
1387                          formatv("(*this->getODSResults({0}).begin())", i));
1388   }
1389 
1390   // Verify the attributes have the correct type.
1391   for (const auto &namedAttr : op.getAttributes()) {
1392     const auto &attr = namedAttr.attr;
1393     if (attr.isDerivedAttr())
1394       continue;
1395 
1396     auto attrName = namedAttr.name;
1397     // Prefix with `tblgen_` to avoid hiding the attribute accessor.
1398     auto varName = tblgenNamePrefix + attrName;
1399     body << formatv("  auto {0} = this->getAttr(\"{1}\");\n", varName,
1400                     attrName);
1401 
1402     bool allowMissingAttr = attr.hasDefaultValue() || attr.isOptional();
1403     if (allowMissingAttr) {
1404       // If the attribute has a default value, then only verify the predicate if
1405       // set. This does effectively assume that the default value is valid.
1406       // TODO: verify the debug value is valid (perhaps in debug mode only).
1407       body << "  if (" << varName << ") {\n";
1408     } else {
1409       body << "  if (!" << varName
1410            << ") return emitOpError(\"requires attribute '" << attrName
1411            << "'\");\n  {\n";
1412     }
1413 
1414     auto attrPred = attr.getPredicate();
1415     if (!attrPred.isNull()) {
1416       body << tgfmt(
1417           "    if (!($0)) return emitOpError(\"attribute '$1' "
1418           "failed to satisfy constraint: $2\");\n",
1419           /*ctx=*/nullptr,
1420           tgfmt(attrPred.getCondition(), &verifyCtx.withSelf(varName)),
1421           attrName, attr.getDescription());
1422     }
1423 
1424     body << "  }\n";
1425   }
1426 
1427   genOperandResultVerifier(body, op.getOperands(), "operand");
1428   genOperandResultVerifier(body, op.getResults(), "result");
1429 
1430   for (auto &trait : op.getTraits()) {
1431     if (auto *t = dyn_cast<tblgen::PredOpTrait>(&trait)) {
1432       body << tgfmt("  if (!($0)) {\n    "
1433                     "return emitOpError(\"failed to verify that $1\");\n  }\n",
1434                     &verifyCtx, tgfmt(t->getPredTemplate(), &verifyCtx),
1435                     t->getDescription());
1436     }
1437   }
1438 
1439   genRegionVerifier(body);
1440   genSuccessorVerifier(body);
1441 
1442   if (hasCustomVerify) {
1443     FmtContext fctx;
1444     fctx.addSubst("cppClass", opClass.getClassName());
1445     auto printer = codeInit->getValue().ltrim().rtrim(" \t\v\f\r");
1446     body << "  " << tgfmt(printer, &fctx);
1447   } else {
1448     body << "  return mlir::success();\n";
1449   }
1450 }
1451 
1452 void OpEmitter::genOperandResultVerifier(OpMethodBody &body,
1453                                          Operator::value_range values,
1454                                          StringRef valueKind) {
1455   FmtContext fctx;
1456 
1457   body << "  {\n";
1458   body << "    unsigned index = 0; (void)index;\n";
1459 
1460   for (auto staticValue : llvm::enumerate(values)) {
1461     bool hasPredicate = staticValue.value().hasPredicate();
1462     bool isOptional = staticValue.value().isOptional();
1463     if (!hasPredicate && !isOptional)
1464       continue;
1465     body << formatv("    auto valueGroup{2} = getODS{0}{1}s({2});\n",
1466                     // Capitalize the first letter to match the function name
1467                     valueKind.substr(0, 1).upper(), valueKind.substr(1),
1468                     staticValue.index());
1469 
1470     // If the constraint is optional check that the value group has at most 1
1471     // value.
1472     if (isOptional) {
1473       body << formatv("    if (valueGroup{0}.size() > 1)\n"
1474                       "      return emitOpError(\"{1} group starting at #\") "
1475                       "<< index << \" requires 0 or 1 element, but found \" << "
1476                       "valueGroup{0}.size();\n",
1477                       staticValue.index(), valueKind);
1478     }
1479 
1480     // Otherwise, if there is no predicate there is nothing left to do.
1481     if (!hasPredicate)
1482       continue;
1483 
1484     // Emit a loop to check all the dynamic values in the pack.
1485     body << "    for (Value v : valueGroup" << staticValue.index() << ") {\n";
1486 
1487     auto constraint = staticValue.value().constraint;
1488     body << "      (void)v;\n"
1489          << "      if (!("
1490          << tgfmt(constraint.getConditionTemplate(),
1491                   &fctx.withSelf("v.getType()"))
1492          << ")) {\n"
1493          << formatv("        return emitOpError(\"{0} #\") << index "
1494                     "<< \" must be {1}, but got \" << v.getType();\n",
1495                     valueKind, constraint.getDescription())
1496          << "      }\n" // if
1497          << "      ++index;\n"
1498          << "    }\n"; // for
1499   }
1500 
1501   body << "  }\n";
1502 }
1503 
1504 void OpEmitter::genRegionVerifier(OpMethodBody &body) {
1505   // If we have no regions, there is nothing more to do.
1506   unsigned numRegions = op.getNumRegions();
1507   if (numRegions == 0)
1508     return;
1509 
1510   body << "{\n";
1511   body << "    unsigned index = 0; (void)index;\n";
1512 
1513   for (unsigned i = 0; i < numRegions; ++i) {
1514     const auto &region = op.getRegion(i);
1515     if (region.constraint.getPredicate().isNull())
1516       continue;
1517 
1518     body << "    for (Region &region : ";
1519     body << formatv(
1520         region.isVariadic()
1521             ? "{0}()"
1522             : "MutableArrayRef<Region>(this->getOperation()->getRegion({1}))",
1523         region.name, i);
1524     body << ") {\n";
1525     auto constraint = tgfmt(region.constraint.getConditionTemplate(),
1526                             &verifyCtx.withSelf("region"))
1527                           .str();
1528 
1529     body << formatv("      (void)region;\n"
1530                     "      if (!({0})) {\n        "
1531                     "return emitOpError(\"region #\") << index << \" {1}"
1532                     "failed to "
1533                     "verify constraint: {2}\";\n      }\n",
1534                     constraint,
1535                     region.name.empty() ? "" : "('" + region.name + "') ",
1536                     region.constraint.getDescription())
1537          << "      ++index;\n"
1538          << "    }\n";
1539   }
1540   body << "  }\n";
1541 }
1542 
1543 void OpEmitter::genSuccessorVerifier(OpMethodBody &body) {
1544   // If we have no successors, there is nothing more to do.
1545   unsigned numSuccessors = op.getNumSuccessors();
1546   if (numSuccessors == 0)
1547     return;
1548 
1549   body << "{\n";
1550   body << "    unsigned index = 0; (void)index;\n";
1551 
1552   for (unsigned i = 0; i < numSuccessors; ++i) {
1553     const auto &successor = op.getSuccessor(i);
1554     if (successor.constraint.getPredicate().isNull())
1555       continue;
1556 
1557     body << "    for (Block *successor : ";
1558     body << formatv(successor.isVariadic() ? "{0}()"
1559                                            : "ArrayRef<Block *>({0}())",
1560                     successor.name);
1561     body << ") {\n";
1562     auto constraint = tgfmt(successor.constraint.getConditionTemplate(),
1563                             &verifyCtx.withSelf("successor"))
1564                           .str();
1565 
1566     body << formatv("      (void)successor;\n"
1567                     "      if (!({0})) {\n        "
1568                     "return emitOpError(\"successor #\") << index << \"('{1}') "
1569                     "failed to "
1570                     "verify constraint: {2}\";\n      }\n",
1571                     constraint, successor.name,
1572                     successor.constraint.getDescription())
1573          << "      ++index;\n"
1574          << "    }\n";
1575   }
1576   body << "  }\n";
1577 }
1578 
1579 /// Add a size count trait to the given operation class.
1580 static void addSizeCountTrait(OpClass &opClass, StringRef traitKind,
1581                               int numTotal, int numVariadic) {
1582   if (numVariadic != 0) {
1583     if (numTotal == numVariadic)
1584       opClass.addTrait("OpTrait::Variadic" + traitKind + "s");
1585     else
1586       opClass.addTrait("OpTrait::AtLeastN" + traitKind + "s<" +
1587                        Twine(numTotal - numVariadic) + ">::Impl");
1588     return;
1589   }
1590   switch (numTotal) {
1591   case 0:
1592     opClass.addTrait("OpTrait::Zero" + traitKind);
1593     break;
1594   case 1:
1595     opClass.addTrait("OpTrait::One" + traitKind);
1596     break;
1597   default:
1598     opClass.addTrait("OpTrait::N" + traitKind + "s<" + Twine(numTotal) +
1599                      ">::Impl");
1600     break;
1601   }
1602 }
1603 
1604 void OpEmitter::genTraits() {
1605   // Add region size trait.
1606   unsigned numRegions = op.getNumRegions();
1607   unsigned numVariadicRegions = op.getNumVariadicRegions();
1608   addSizeCountTrait(opClass, "Region", numRegions, numVariadicRegions);
1609 
1610   // Add result size trait.
1611   int numResults = op.getNumResults();
1612   int numVariadicResults = op.getNumVariableLengthResults();
1613   addSizeCountTrait(opClass, "Result", numResults, numVariadicResults);
1614 
1615   // Add successor size trait.
1616   unsigned numSuccessors = op.getNumSuccessors();
1617   unsigned numVariadicSuccessors = op.getNumVariadicSuccessors();
1618   addSizeCountTrait(opClass, "Successor", numSuccessors, numVariadicSuccessors);
1619 
1620   // Add variadic size trait and normal op traits.
1621   int numOperands = op.getNumOperands();
1622   int numVariadicOperands = op.getNumVariableLengthOperands();
1623 
1624   // Add operand size trait.
1625   if (numVariadicOperands != 0) {
1626     if (numOperands == numVariadicOperands)
1627       opClass.addTrait("OpTrait::VariadicOperands");
1628     else
1629       opClass.addTrait("OpTrait::AtLeastNOperands<" +
1630                        Twine(numOperands - numVariadicOperands) + ">::Impl");
1631   } else {
1632     switch (numOperands) {
1633     case 0:
1634       opClass.addTrait("OpTrait::ZeroOperands");
1635       break;
1636     case 1:
1637       opClass.addTrait("OpTrait::OneOperand");
1638       break;
1639     default:
1640       opClass.addTrait("OpTrait::NOperands<" + Twine(numOperands) + ">::Impl");
1641       break;
1642     }
1643   }
1644 
1645   // Add the native and interface traits.
1646   for (const auto &trait : op.getTraits()) {
1647     if (auto opTrait = dyn_cast<tblgen::NativeOpTrait>(&trait))
1648       opClass.addTrait(opTrait->getTrait());
1649     else if (auto opTrait = dyn_cast<tblgen::InterfaceOpTrait>(&trait))
1650       opClass.addTrait(opTrait->getTrait());
1651   }
1652 }
1653 
1654 void OpEmitter::genOpNameGetter() {
1655   auto &method = opClass.newMethod("StringRef", "getOperationName",
1656                                    /*params=*/"", OpMethod::MP_Static);
1657   method.body() << "  return \"" << op.getOperationName() << "\";\n";
1658 }
1659 
1660 void OpEmitter::genOpAsmInterface() {
1661   // If the user only has one results or specifically added the Asm trait,
1662   // then don't generate it for them. We specifically only handle multi result
1663   // operations, because the name of a single result in the common case is not
1664   // interesting(generally 'result'/'output'/etc.).
1665   // TODO: We could also add a flag to allow operations to opt in to this
1666   // generation, even if they only have a single operation.
1667   int numResults = op.getNumResults();
1668   if (numResults <= 1 || op.getTrait("OpAsmOpInterface::Trait"))
1669     return;
1670 
1671   SmallVector<StringRef, 4> resultNames(numResults);
1672   for (int i = 0; i != numResults; ++i)
1673     resultNames[i] = op.getResultName(i);
1674 
1675   // Don't add the trait if none of the results have a valid name.
1676   if (llvm::all_of(resultNames, [](StringRef name) { return name.empty(); }))
1677     return;
1678   opClass.addTrait("OpAsmOpInterface::Trait");
1679 
1680   // Generate the right accessor for the number of results.
1681   auto &method = opClass.newMethod("void", "getAsmResultNames",
1682                                    "OpAsmSetValueNameFn setNameFn");
1683   auto &body = method.body();
1684   for (int i = 0; i != numResults; ++i) {
1685     body << "  auto resultGroup" << i << " = getODSResults(" << i << ");\n"
1686          << "  if (!llvm::empty(resultGroup" << i << "))\n"
1687          << "    setNameFn(*resultGroup" << i << ".begin(), \""
1688          << resultNames[i] << "\");\n";
1689   }
1690 }
1691 
1692 //===----------------------------------------------------------------------===//
1693 // OpOperandAdaptor emitter
1694 //===----------------------------------------------------------------------===//
1695 
1696 namespace {
1697 // Helper class to emit Op operand adaptors to an output stream.  Operand
1698 // adaptors are wrappers around ArrayRef<Value> that provide named operand
1699 // getters identical to those defined in the Op.
1700 class OpOperandAdaptorEmitter {
1701 public:
1702   static void emitDecl(const Operator &op, raw_ostream &os);
1703   static void emitDef(const Operator &op, raw_ostream &os);
1704 
1705 private:
1706   explicit OpOperandAdaptorEmitter(const Operator &op);
1707 
1708   Class adapterClass;
1709 };
1710 } // end namespace
1711 
1712 OpOperandAdaptorEmitter::OpOperandAdaptorEmitter(const Operator &op)
1713     : adapterClass(op.getCppClassName().str() + "OperandAdaptor") {
1714   adapterClass.newField("ArrayRef<Value>", "tblgen_operands");
1715   auto &constructor = adapterClass.newConstructor("ArrayRef<Value> values");
1716   constructor.body() << "  tblgen_operands = values;\n";
1717 
1718   generateNamedOperandGetters(op, adapterClass,
1719                               /*rangeType=*/"ArrayRef<Value>",
1720                               /*rangeBeginCall=*/"tblgen_operands.begin()",
1721                               /*rangeSizeCall=*/"tblgen_operands.size()",
1722                               /*getOperandCallPattern=*/"tblgen_operands[{0}]");
1723 }
1724 
1725 void OpOperandAdaptorEmitter::emitDecl(const Operator &op, raw_ostream &os) {
1726   OpOperandAdaptorEmitter(op).adapterClass.writeDeclTo(os);
1727 }
1728 
1729 void OpOperandAdaptorEmitter::emitDef(const Operator &op, raw_ostream &os) {
1730   OpOperandAdaptorEmitter(op).adapterClass.writeDefTo(os);
1731 }
1732 
1733 // Emits the opcode enum and op classes.
1734 static void emitOpClasses(const std::vector<Record *> &defs, raw_ostream &os,
1735                           bool emitDecl) {
1736   IfDefScope scope("GET_OP_CLASSES", os);
1737   // First emit forward declaration for each class, this allows them to refer
1738   // to each others in traits for example.
1739   if (emitDecl) {
1740     for (auto *def : defs) {
1741       Operator op(*def);
1742       os << "class " << op.getCppClassName() << ";\n";
1743     }
1744   }
1745   for (auto *def : defs) {
1746     Operator op(*def);
1747     const auto *attrSizedOperands =
1748         op.getTrait("OpTrait::AttrSizedOperandSegments");
1749     if (emitDecl) {
1750       os << formatv(opCommentHeader, op.getQualCppClassName(), "declarations");
1751       // We cannot generate the operand adaptor class if operand getters depend
1752       // on an attribute.
1753       if (!attrSizedOperands)
1754         OpOperandAdaptorEmitter::emitDecl(op, os);
1755       OpEmitter::emitDecl(op, os);
1756     } else {
1757       os << formatv(opCommentHeader, op.getQualCppClassName(), "definitions");
1758       if (!attrSizedOperands)
1759         OpOperandAdaptorEmitter::emitDef(op, os);
1760       OpEmitter::emitDef(op, os);
1761     }
1762   }
1763 }
1764 
1765 // Emits a comma-separated list of the ops.
1766 static void emitOpList(const std::vector<Record *> &defs, raw_ostream &os) {
1767   IfDefScope scope("GET_OP_LIST", os);
1768 
1769   llvm::interleave(
1770       // TODO: We are constructing the Operator wrapper instance just for
1771       // getting it's qualified class name here. Reduce the overhead by having a
1772       // lightweight version of Operator class just for that purpose.
1773       defs, [&os](Record *def) { os << Operator(def).getQualCppClassName(); },
1774       [&os]() { os << ",\n"; });
1775 }
1776 
1777 static bool emitOpDecls(const RecordKeeper &recordKeeper, raw_ostream &os) {
1778   emitSourceFileHeader("Op Declarations", os);
1779 
1780   const auto &defs = recordKeeper.getAllDerivedDefinitions("Op");
1781   emitOpClasses(defs, os, /*emitDecl=*/true);
1782 
1783   return false;
1784 }
1785 
1786 static bool emitOpDefs(const RecordKeeper &recordKeeper, raw_ostream &os) {
1787   emitSourceFileHeader("Op Definitions", os);
1788 
1789   const auto &defs = recordKeeper.getAllDerivedDefinitions("Op");
1790   emitOpList(defs, os);
1791   emitOpClasses(defs, os, /*emitDecl=*/false);
1792 
1793   return false;
1794 }
1795 
1796 static mlir::GenRegistration
1797     genOpDecls("gen-op-decls", "Generate op declarations",
1798                [](const RecordKeeper &records, raw_ostream &os) {
1799                  return emitOpDecls(records, os);
1800                });
1801 
1802 static mlir::GenRegistration genOpDefs("gen-op-defs", "Generate op definitions",
1803                                        [](const RecordKeeper &records,
1804                                           raw_ostream &os) {
1805                                          return emitOpDefs(records, os);
1806                                        });
1807