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