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