xref: /llvm-project/llvm/utils/TableGen/OptionParserEmitter.cpp (revision f4de28a63c81c909df28b6b065fad19e2189c54e)
1 //===- OptionParserEmitter.cpp - Table Driven Command Option Line Parsing -===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Common/OptEmitter.h"
10 #include "llvm/ADT/STLExtras.h"
11 #include "llvm/ADT/SmallString.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/ADT/Twine.h"
14 #include "llvm/Support/raw_ostream.h"
15 #include "llvm/TableGen/Record.h"
16 #include "llvm/TableGen/StringToOffsetTable.h"
17 #include "llvm/TableGen/TableGenBackend.h"
18 #include <cstring>
19 #include <map>
20 
21 using namespace llvm;
22 
23 static std::string getOptionName(const Record &R) {
24   // Use the record name unless EnumName is defined.
25   if (isa<UnsetInit>(R.getValueInit("EnumName")))
26     return std::string(R.getName());
27 
28   return std::string(R.getValueAsString("EnumName"));
29 }
30 
31 static raw_ostream &writeStrTableOffset(raw_ostream &OS,
32                                         const StringToOffsetTable &Table,
33                                         llvm::StringRef Str) {
34   OS << Table.GetStringOffset(Str) << " /* ";
35   OS.write_escaped(Str);
36   OS << " */";
37   return OS;
38 }
39 
40 static raw_ostream &writeCstring(raw_ostream &OS, llvm::StringRef Str) {
41   OS << '"';
42   OS.write_escaped(Str);
43   OS << '"';
44   return OS;
45 }
46 
47 static std::string getOptionPrefixedName(const Record &R) {
48   std::vector<StringRef> Prefixes = R.getValueAsListOfStrings("Prefixes");
49   StringRef Name = R.getValueAsString("Name");
50 
51   if (Prefixes.empty())
52     return Name.str();
53 
54   return (Prefixes[0] + Twine(Name)).str();
55 }
56 
57 class MarshallingInfo {
58 public:
59   static constexpr const char *MacroName = "OPTION_WITH_MARSHALLING";
60   const Record &R;
61   bool ShouldAlwaysEmit = false;
62   StringRef MacroPrefix;
63   StringRef KeyPath;
64   StringRef DefaultValue;
65   StringRef NormalizedValuesScope;
66   StringRef ImpliedCheck;
67   StringRef ImpliedValue;
68   StringRef ShouldParse;
69   StringRef Normalizer;
70   StringRef Denormalizer;
71   StringRef ValueMerger;
72   StringRef ValueExtractor;
73   int TableIndex = -1;
74   std::vector<StringRef> Values;
75   std::vector<StringRef> NormalizedValues;
76   std::string ValueTableName;
77 
78   static size_t NextTableIndex;
79 
80   static constexpr const char *ValueTablePreamble = R"(
81 struct SimpleEnumValue {
82   const char *Name;
83   unsigned Value;
84 };
85 
86 struct SimpleEnumValueTable {
87   const SimpleEnumValue *Table;
88   unsigned Size;
89 };
90 )";
91 
92   static constexpr const char *ValueTablesDecl =
93       "static const SimpleEnumValueTable SimpleEnumValueTables[] = ";
94 
95   MarshallingInfo(const Record &R) : R(R) {}
96 
97   std::string getMacroName() const {
98     return (MacroPrefix + MarshallingInfo::MacroName).str();
99   }
100 
101   void emit(raw_ostream &OS) const {
102     OS << ShouldParse;
103     OS << ", ";
104     OS << ShouldAlwaysEmit;
105     OS << ", ";
106     OS << KeyPath;
107     OS << ", ";
108     emitScopedNormalizedValue(OS, DefaultValue);
109     OS << ", ";
110     OS << ImpliedCheck;
111     OS << ", ";
112     emitScopedNormalizedValue(OS, ImpliedValue);
113     OS << ", ";
114     OS << Normalizer;
115     OS << ", ";
116     OS << Denormalizer;
117     OS << ", ";
118     OS << ValueMerger;
119     OS << ", ";
120     OS << ValueExtractor;
121     OS << ", ";
122     OS << TableIndex;
123   }
124 
125   std::optional<StringRef> emitValueTable(raw_ostream &OS) const {
126     if (TableIndex == -1)
127       return {};
128     OS << "static const SimpleEnumValue " << ValueTableName << "[] = {\n";
129     for (unsigned I = 0, E = Values.size(); I != E; ++I) {
130       OS << "{";
131       writeCstring(OS, Values[I]);
132       OS << ",";
133       OS << "static_cast<unsigned>(";
134       emitScopedNormalizedValue(OS, NormalizedValues[I]);
135       OS << ")},";
136     }
137     OS << "};\n";
138     return StringRef(ValueTableName);
139   }
140 
141 private:
142   void emitScopedNormalizedValue(raw_ostream &OS,
143                                  StringRef NormalizedValue) const {
144     if (!NormalizedValuesScope.empty())
145       OS << NormalizedValuesScope << "::";
146     OS << NormalizedValue;
147   }
148 };
149 
150 size_t MarshallingInfo::NextTableIndex = 0;
151 
152 static MarshallingInfo createMarshallingInfo(const Record &R) {
153   assert(!isa<UnsetInit>(R.getValueInit("KeyPath")) &&
154          !isa<UnsetInit>(R.getValueInit("DefaultValue")) &&
155          !isa<UnsetInit>(R.getValueInit("ValueMerger")) &&
156          "MarshallingInfo must have a provide a keypath, default value and a "
157          "value merger");
158 
159   MarshallingInfo Ret(R);
160 
161   Ret.ShouldAlwaysEmit = R.getValueAsBit("ShouldAlwaysEmit");
162   Ret.MacroPrefix = R.getValueAsString("MacroPrefix");
163   Ret.KeyPath = R.getValueAsString("KeyPath");
164   Ret.DefaultValue = R.getValueAsString("DefaultValue");
165   Ret.NormalizedValuesScope = R.getValueAsString("NormalizedValuesScope");
166   Ret.ImpliedCheck = R.getValueAsString("ImpliedCheck");
167   Ret.ImpliedValue =
168       R.getValueAsOptionalString("ImpliedValue").value_or(Ret.DefaultValue);
169 
170   Ret.ShouldParse = R.getValueAsString("ShouldParse");
171   Ret.Normalizer = R.getValueAsString("Normalizer");
172   Ret.Denormalizer = R.getValueAsString("Denormalizer");
173   Ret.ValueMerger = R.getValueAsString("ValueMerger");
174   Ret.ValueExtractor = R.getValueAsString("ValueExtractor");
175 
176   if (!isa<UnsetInit>(R.getValueInit("NormalizedValues"))) {
177     assert(!isa<UnsetInit>(R.getValueInit("Values")) &&
178            "Cannot provide normalized values for value-less options");
179     Ret.TableIndex = MarshallingInfo::NextTableIndex++;
180     Ret.NormalizedValues = R.getValueAsListOfStrings("NormalizedValues");
181     Ret.Values.reserve(Ret.NormalizedValues.size());
182     Ret.ValueTableName = getOptionName(R) + "ValueTable";
183 
184     StringRef ValuesStr = R.getValueAsString("Values");
185     for (;;) {
186       size_t Idx = ValuesStr.find(',');
187       if (Idx == StringRef::npos)
188         break;
189       if (Idx > 0)
190         Ret.Values.push_back(ValuesStr.slice(0, Idx));
191       ValuesStr = ValuesStr.substr(Idx + 1);
192     }
193     if (!ValuesStr.empty())
194       Ret.Values.push_back(ValuesStr);
195 
196     assert(Ret.Values.size() == Ret.NormalizedValues.size() &&
197            "The number of normalized values doesn't match the number of "
198            "values");
199   }
200 
201   return Ret;
202 }
203 
204 static void emitHelpTextsForVariants(
205     raw_ostream &OS, std::vector<std::pair<std::vector<std::string>, StringRef>>
206                          HelpTextsForVariants) {
207   // OptTable must be constexpr so it uses std::arrays with these capacities.
208   const unsigned MaxVisibilityPerHelp = 2;
209   const unsigned MaxVisibilityHelp = 1;
210 
211   assert(HelpTextsForVariants.size() <= MaxVisibilityHelp &&
212          "Too many help text variants to store in "
213          "OptTable::HelpTextsForVariants");
214 
215   // This function must initialise any unused elements of those arrays.
216   for (auto [Visibilities, _] : HelpTextsForVariants)
217     while (Visibilities.size() < MaxVisibilityPerHelp)
218       Visibilities.push_back("0");
219 
220   while (HelpTextsForVariants.size() < MaxVisibilityHelp)
221     HelpTextsForVariants.push_back(
222         {std::vector<std::string>(MaxVisibilityPerHelp, "0"), ""});
223 
224   OS << ", (std::array<std::pair<std::array<unsigned, " << MaxVisibilityPerHelp
225      << ">, const char*>, " << MaxVisibilityHelp << ">{{ ";
226 
227   auto VisibilityHelpEnd = HelpTextsForVariants.cend();
228   for (auto VisibilityHelp = HelpTextsForVariants.cbegin();
229        VisibilityHelp != VisibilityHelpEnd; ++VisibilityHelp) {
230     auto [Visibilities, Help] = *VisibilityHelp;
231 
232     assert(Visibilities.size() <= MaxVisibilityPerHelp &&
233            "Too many visibilities to store in an "
234            "OptTable::HelpTextsForVariants entry");
235     OS << "{std::array<unsigned, " << MaxVisibilityPerHelp << ">{{";
236 
237     auto VisibilityEnd = Visibilities.cend();
238     for (auto Visibility = Visibilities.cbegin(); Visibility != VisibilityEnd;
239          ++Visibility) {
240       OS << *Visibility;
241       if (std::next(Visibility) != VisibilityEnd)
242         OS << ", ";
243     }
244 
245     OS << "}}, ";
246 
247     if (Help.size())
248       writeCstring(OS, Help);
249     else
250       OS << "nullptr";
251     OS << "}";
252 
253     if (std::next(VisibilityHelp) != VisibilityHelpEnd)
254       OS << ", ";
255   }
256   OS << " }})";
257 }
258 
259 /// OptionParserEmitter - This tablegen backend takes an input .td file
260 /// describing a list of options and emits a data structure for parsing and
261 /// working with those options when given an input command line.
262 static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
263   // Get the option groups and options.
264   ArrayRef<const Record *> Groups =
265       Records.getAllDerivedDefinitions("OptionGroup");
266   std::vector<const Record *> Opts = Records.getAllDerivedDefinitions("Option");
267   llvm::sort(Opts, IsOptionRecordsLess);
268 
269   emitSourceFileHeader("Option Parsing Definitions", OS);
270 
271   // Generate prefix groups.
272   typedef SmallVector<SmallString<2>, 2> PrefixKeyT;
273   typedef std::map<PrefixKeyT, unsigned> PrefixesT;
274   PrefixesT Prefixes;
275   Prefixes.insert({PrefixKeyT(), 0});
276   for (const Record &R : llvm::make_pointee_range(Opts)) {
277     std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");
278     PrefixKeyT PrefixKey(RPrefixes.begin(), RPrefixes.end());
279     Prefixes.insert({PrefixKey, 0});
280   }
281 
282   DenseSet<StringRef> PrefixesUnionSet;
283   for (const auto &[Prefix, _] : Prefixes)
284     PrefixesUnionSet.insert(Prefix.begin(), Prefix.end());
285   SmallVector<StringRef> PrefixesUnion(PrefixesUnionSet.begin(),
286                                        PrefixesUnionSet.end());
287   array_pod_sort(PrefixesUnion.begin(), PrefixesUnion.end());
288 
289   llvm::StringToOffsetTable Table;
290   // We can add all the prefixes via the union.
291   for (const auto &Prefix : PrefixesUnion)
292     Table.GetOrAddStringOffset(Prefix);
293   for (const Record &R : llvm::make_pointee_range(Groups))
294     Table.GetOrAddStringOffset(R.getValueAsString("Name"));
295   for (const Record &R : llvm::make_pointee_range(Opts))
296     Table.GetOrAddStringOffset(getOptionPrefixedName(R));
297 
298   // Dump string table.
299   OS << "/////////\n";
300   OS << "// String table\n\n";
301   OS << "#ifdef OPTTABLE_STR_TABLE_CODE\n";
302   Table.EmitStringTableDef(OS, "OptionStrTable", /*Indent=*/"");
303   OS << "#endif // OPTTABLE_STR_TABLE_CODE\n\n";
304 
305   // Dump prefixes.
306   OS << "/////////\n";
307   OS << "// Prefixes\n\n";
308   OS << "#ifdef OPTTABLE_PREFIXES_TABLE_CODE\n";
309   OS << "static constexpr llvm::StringTable::Offset OptionPrefixesTable[] = "
310         "{\n";
311   {
312     // Ensure the first prefix set is always empty.
313     assert(!Prefixes.empty() &&
314            "We should always emit an empty set of prefixes");
315     assert(Prefixes.begin()->first.empty() &&
316            "First prefix set should always be empty");
317     llvm::ListSeparator Sep(",\n");
318     unsigned CurIndex = 0;
319     for (auto &[Prefix, PrefixIndex] : Prefixes) {
320       // First emit the number of prefix strings in this list of prefixes.
321       OS << Sep << "  " << Prefix.size() << " /* prefixes */";
322       PrefixIndex = CurIndex;
323       assert((CurIndex == 0 || !Prefix.empty()) &&
324              "Only first prefix set should be empty!");
325       for (const auto &PrefixKey : Prefix)
326         OS << ", " << *Table.GetStringOffset(PrefixKey) << " /* '" << PrefixKey
327            << "' */";
328       CurIndex += Prefix.size() + 1;
329     }
330   }
331   OS << "\n};\n";
332   OS << "#endif // OPTTABLE_PREFIXES_TABLE_CODE\n\n";
333 
334   // Dump prefixes union.
335   OS << "/////////\n";
336   OS << "// Prefix Union\n\n";
337   OS << "#ifdef OPTTABLE_PREFIXES_UNION_CODE\n";
338   OS << "static constexpr llvm::StringTable::Offset OptionPrefixesUnion[] = "
339         "{\n";
340   {
341     llvm::ListSeparator Sep(", ");
342     for (auto Prefix : PrefixesUnion)
343       OS << Sep << "  " << *Table.GetStringOffset(Prefix) << " /* '" << Prefix
344          << "' */";
345   }
346   OS << "\n};\n";
347   OS << "#endif // OPTTABLE_PREFIXES_UNION_CODE\n\n";
348 
349   // Dump groups.
350   OS << "/////////\n";
351   OS << "// ValuesCode\n\n";
352   OS << "#ifdef OPTTABLE_VALUES_CODE\n";
353   for (const Record &R : llvm::make_pointee_range(Opts)) {
354     // The option values, if any;
355     if (!isa<UnsetInit>(R.getValueInit("ValuesCode"))) {
356       assert(isa<UnsetInit>(R.getValueInit("Values")) &&
357              "Cannot choose between Values and ValuesCode");
358       OS << "#define VALUES_CODE " << getOptionName(R) << "_Values\n";
359       OS << R.getValueAsString("ValuesCode") << "\n";
360       OS << "#undef VALUES_CODE\n";
361     }
362   }
363   OS << "#endif\n";
364 
365   OS << "/////////\n";
366   OS << "// Groups\n\n";
367   OS << "#ifdef OPTION\n";
368   for (const Record &R : llvm::make_pointee_range(Groups)) {
369     // Start a single option entry.
370     OS << "OPTION(";
371 
372     // A zero prefix offset corresponds to an empty set of prefixes.
373     OS << "0 /* no prefixes */";
374 
375     // The option string offset.
376     OS << ", ";
377     writeStrTableOffset(OS, Table, R.getValueAsString("Name"));
378 
379     // The option identifier name.
380     OS << ", " << getOptionName(R);
381 
382     // The option kind.
383     OS << ", Group";
384 
385     // The containing option group (if any).
386     OS << ", ";
387     if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group")))
388       OS << getOptionName(*DI->getDef());
389     else
390       OS << "INVALID";
391 
392     // The other option arguments (unused for groups).
393     OS << ", INVALID, nullptr, 0, 0, 0";
394 
395     // The option help text.
396     if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {
397       OS << ",\n";
398       OS << "       ";
399       writeCstring(OS, R.getValueAsString("HelpText"));
400     } else
401       OS << ", nullptr";
402 
403     // Not using Visibility specific text for group help.
404     emitHelpTextsForVariants(OS, {});
405 
406     // The option meta-variable name (unused).
407     OS << ", nullptr";
408 
409     // The option Values (unused for groups).
410     OS << ", nullptr)\n";
411   }
412   OS << "\n";
413 
414   OS << "//////////\n";
415   OS << "// Options\n\n";
416 
417   auto WriteOptRecordFields = [&](raw_ostream &OS, const Record &R) {
418     // The option prefix;
419     std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");
420     OS << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())] << ", ";
421 
422     // The option prefixed name.
423     writeStrTableOffset(OS, Table, getOptionPrefixedName(R));
424 
425     // The option identifier name.
426     OS << ", " << getOptionName(R);
427 
428     // The option kind.
429     OS << ", " << R.getValueAsDef("Kind")->getValueAsString("Name");
430 
431     // The containing option group (if any).
432     OS << ", ";
433     const ListInit *GroupFlags = nullptr;
434     const ListInit *GroupVis = nullptr;
435     if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
436       GroupFlags = DI->getDef()->getValueAsListInit("Flags");
437       GroupVis = DI->getDef()->getValueAsListInit("Visibility");
438       OS << getOptionName(*DI->getDef());
439     } else
440       OS << "INVALID";
441 
442     // The option alias (if any).
443     OS << ", ";
444     if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Alias")))
445       OS << getOptionName(*DI->getDef());
446     else
447       OS << "INVALID";
448 
449     // The option alias arguments (if any).
450     // Emitted as a \0 separated list in a string, e.g. ["foo", "bar"]
451     // would become "foo\0bar\0". Note that the compiler adds an implicit
452     // terminating \0 at the end.
453     OS << ", ";
454     std::vector<StringRef> AliasArgs = R.getValueAsListOfStrings("AliasArgs");
455     if (AliasArgs.size() == 0) {
456       OS << "nullptr";
457     } else {
458       OS << "\"";
459       for (StringRef AliasArg : AliasArgs)
460         OS << AliasArg << "\\0";
461       OS << "\"";
462     }
463 
464     // "Flags" for the option, such as HelpHidden and Render*
465     OS << ", ";
466     int NumFlags = 0;
467     const ListInit *LI = R.getValueAsListInit("Flags");
468     for (const Init *I : *LI)
469       OS << (NumFlags++ ? " | " : "") << cast<DefInit>(I)->getDef()->getName();
470     if (GroupFlags) {
471       for (const Init *I : *GroupFlags)
472         OS << (NumFlags++ ? " | " : "")
473            << cast<DefInit>(I)->getDef()->getName();
474     }
475     if (NumFlags == 0)
476       OS << '0';
477 
478     // Option visibility, for sharing options between drivers.
479     OS << ", ";
480     int NumVisFlags = 0;
481     LI = R.getValueAsListInit("Visibility");
482     for (const Init *I : *LI)
483       OS << (NumVisFlags++ ? " | " : "")
484          << cast<DefInit>(I)->getDef()->getName();
485     if (GroupVis) {
486       for (const Init *I : *GroupVis)
487         OS << (NumVisFlags++ ? " | " : "")
488            << cast<DefInit>(I)->getDef()->getName();
489     }
490     if (NumVisFlags == 0)
491       OS << '0';
492 
493     // The option parameter field.
494     OS << ", " << R.getValueAsInt("NumArgs");
495 
496     // The option help text.
497     if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {
498       OS << ",\n";
499       OS << "       ";
500       writeCstring(OS, R.getValueAsString("HelpText"));
501     } else
502       OS << ", nullptr";
503 
504     std::vector<std::pair<std::vector<std::string>, StringRef>>
505         HelpTextsForVariants;
506     for (const Record *VisibilityHelp :
507          R.getValueAsListOfDefs("HelpTextsForVariants")) {
508       ArrayRef<const Init *> Visibilities =
509           VisibilityHelp->getValueAsListInit("Visibilities")->getValues();
510 
511       std::vector<std::string> VisibilityNames;
512       for (const Init *Visibility : Visibilities)
513         VisibilityNames.push_back(Visibility->getAsUnquotedString());
514 
515       HelpTextsForVariants.emplace_back(
516           VisibilityNames, VisibilityHelp->getValueAsString("Text"));
517     }
518     emitHelpTextsForVariants(OS, std::move(HelpTextsForVariants));
519 
520     // The option meta-variable name.
521     OS << ", ";
522     if (!isa<UnsetInit>(R.getValueInit("MetaVarName")))
523       writeCstring(OS, R.getValueAsString("MetaVarName"));
524     else
525       OS << "nullptr";
526 
527     // The option Values. Used for shell autocompletion.
528     OS << ", ";
529     if (!isa<UnsetInit>(R.getValueInit("Values")))
530       writeCstring(OS, R.getValueAsString("Values"));
531     else if (!isa<UnsetInit>(R.getValueInit("ValuesCode"))) {
532       OS << getOptionName(R) << "_Values";
533     } else
534       OS << "nullptr";
535   };
536 
537   auto IsMarshallingOption = [](const Record &R) {
538     return !isa<UnsetInit>(R.getValueInit("KeyPath")) &&
539            !R.getValueAsString("KeyPath").empty();
540   };
541 
542   std::vector<const Record *> OptsWithMarshalling;
543   for (const Record &R : llvm::make_pointee_range(Opts)) {
544     // Start a single option entry.
545     OS << "OPTION(";
546     WriteOptRecordFields(OS, R);
547     OS << ")\n";
548     if (IsMarshallingOption(R))
549       OptsWithMarshalling.push_back(&R);
550   }
551   OS << "#endif // OPTION\n";
552 
553   auto CmpMarshallingOpts = [](const Record *const *A, const Record *const *B) {
554     unsigned AID = (*A)->getID();
555     unsigned BID = (*B)->getID();
556 
557     if (AID < BID)
558       return -1;
559     if (AID > BID)
560       return 1;
561     return 0;
562   };
563   // The RecordKeeper stores records (options) in lexicographical order, and we
564   // have reordered the options again when generating prefix groups. We need to
565   // restore the original definition order of options with marshalling to honor
566   // the topology of the dependency graph implied by `DefaultAnyOf`.
567   array_pod_sort(OptsWithMarshalling.begin(), OptsWithMarshalling.end(),
568                  CmpMarshallingOpts);
569 
570   std::vector<MarshallingInfo> MarshallingInfos;
571   MarshallingInfos.reserve(OptsWithMarshalling.size());
572   for (const auto *R : OptsWithMarshalling)
573     MarshallingInfos.push_back(createMarshallingInfo(*R));
574 
575   for (const auto &MI : MarshallingInfos) {
576     OS << "#ifdef " << MI.getMacroName() << "\n";
577     OS << MI.getMacroName() << "(";
578     WriteOptRecordFields(OS, MI.R);
579     OS << ", ";
580     MI.emit(OS);
581     OS << ")\n";
582     OS << "#endif // " << MI.getMacroName() << "\n";
583   }
584 
585   OS << "\n";
586   OS << "#ifdef SIMPLE_ENUM_VALUE_TABLE";
587   OS << "\n";
588   OS << MarshallingInfo::ValueTablePreamble;
589   std::vector<StringRef> ValueTableNames;
590   for (const auto &MI : MarshallingInfos)
591     if (auto MaybeValueTableName = MI.emitValueTable(OS))
592       ValueTableNames.push_back(*MaybeValueTableName);
593 
594   OS << MarshallingInfo::ValueTablesDecl << "{";
595   for (auto ValueTableName : ValueTableNames)
596     OS << "{" << ValueTableName << ", std::size(" << ValueTableName << ")},\n";
597   OS << "};\n";
598   OS << "static const unsigned SimpleEnumValueTablesSize = "
599         "std::size(SimpleEnumValueTables);\n";
600 
601   OS << "#endif // SIMPLE_ENUM_VALUE_TABLE\n";
602   OS << "\n";
603 
604   OS << "\n";
605 }
606 
607 static TableGen::Emitter::Opt X("gen-opt-parser-defs", emitOptionParser,
608                                 "Generate option definitions");
609