[LLD][COFF] Emit tail merge pdata for delay load thunks on ARM64EC (#116810)
[llvm-project.git] / clang / utils / TableGen / ClangOptionDocEmitter.cpp
blobe08fb11df310023930cf39840834fa2883666352
1 //===-- ClangOptionDocEmitter.cpp - Documentation for command line flags --===//
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 // FIXME: Once this has stabilized, consider moving it to LLVM.
8 //
9 //===----------------------------------------------------------------------===//
11 #include "TableGenBackends.h"
12 #include "llvm/ADT/STLExtras.h"
13 #include "llvm/ADT/StringSwitch.h"
14 #include "llvm/ADT/Twine.h"
15 #include "llvm/TableGen/Error.h"
16 #include "llvm/TableGen/Record.h"
17 #include "llvm/TableGen/TableGenBackend.h"
18 #include <cctype>
19 #include <cstring>
20 #include <map>
22 using namespace llvm;
24 namespace {
25 struct DocumentedOption {
26 const Record *Option;
27 std::vector<const Record *> Aliases;
29 struct DocumentedGroup;
30 struct Documentation {
31 std::vector<DocumentedGroup> Groups;
32 std::vector<DocumentedOption> Options;
34 bool empty() {
35 return Groups.empty() && Options.empty();
38 struct DocumentedGroup : Documentation {
39 const Record *Group;
42 static bool hasFlag(const Record *Option, StringRef OptionFlag,
43 StringRef FlagsField) {
44 for (const Record *Flag : Option->getValueAsListOfDefs(FlagsField))
45 if (Flag->getName() == OptionFlag)
46 return true;
47 if (const DefInit *DI = dyn_cast<DefInit>(Option->getValueInit("Group")))
48 for (const Record *Flag : DI->getDef()->getValueAsListOfDefs(FlagsField))
49 if (Flag->getName() == OptionFlag)
50 return true;
51 return false;
54 static bool isOptionVisible(const Record *Option, const Record *DocInfo) {
55 for (StringRef IgnoredFlag : DocInfo->getValueAsListOfStrings("IgnoreFlags"))
56 if (hasFlag(Option, IgnoredFlag, "Flags"))
57 return false;
58 for (StringRef Mask : DocInfo->getValueAsListOfStrings("VisibilityMask"))
59 if (hasFlag(Option, Mask, "Visibility"))
60 return true;
61 return false;
64 // Reorganize the records into a suitable form for emitting documentation.
65 Documentation extractDocumentation(const RecordKeeper &Records,
66 const Record *DocInfo) {
67 Documentation Result;
69 // Build the tree of groups. The root in the tree is the fake option group
70 // (Record*)nullptr, which contains all top-level groups and options.
71 std::map<const Record *, std::vector<const Record *>> OptionsInGroup;
72 std::map<const Record *, std::vector<const Record *>> GroupsInGroup;
73 std::map<const Record *, std::vector<const Record *>> Aliases;
75 std::map<std::string, const Record *> OptionsByName;
76 for (const Record *R : Records.getAllDerivedDefinitions("Option"))
77 OptionsByName[std::string(R->getValueAsString("Name"))] = R;
79 auto Flatten = [](const Record *R) {
80 return R->getValue("DocFlatten") && R->getValueAsBit("DocFlatten");
83 auto SkipFlattened = [&](const Record *R) -> const Record * {
84 while (R && Flatten(R)) {
85 auto *G = dyn_cast<DefInit>(R->getValueInit("Group"));
86 if (!G)
87 return nullptr;
88 R = G->getDef();
90 return R;
93 for (const Record *R : Records.getAllDerivedDefinitions("OptionGroup")) {
94 if (Flatten(R))
95 continue;
97 const Record *Group = nullptr;
98 if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group")))
99 Group = SkipFlattened(G->getDef());
100 GroupsInGroup[Group].push_back(R);
103 for (const Record *R : Records.getAllDerivedDefinitions("Option")) {
104 if (auto *A = dyn_cast<DefInit>(R->getValueInit("Alias"))) {
105 Aliases[A->getDef()].push_back(R);
106 continue;
109 // Pretend no-X and Xno-Y options are aliases of X and XY.
110 std::string Name = std::string(R->getValueAsString("Name"));
111 if (Name.size() >= 4) {
112 if (Name.substr(0, 3) == "no-" && OptionsByName[Name.substr(3)]) {
113 Aliases[OptionsByName[Name.substr(3)]].push_back(R);
114 continue;
116 if (Name.substr(1, 3) == "no-" && OptionsByName[Name[0] + Name.substr(4)]) {
117 Aliases[OptionsByName[Name[0] + Name.substr(4)]].push_back(R);
118 continue;
122 const Record *Group = nullptr;
123 if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group")))
124 Group = SkipFlattened(G->getDef());
125 OptionsInGroup[Group].push_back(R);
128 auto CompareByName = [](const Record *A, const Record *B) {
129 return A->getValueAsString("Name") < B->getValueAsString("Name");
132 auto CompareByLocation = [](const Record *A, const Record *B) {
133 return A->getLoc()[0].getPointer() < B->getLoc()[0].getPointer();
136 auto DocumentationForOption = [&](const Record *R) -> DocumentedOption {
137 auto &A = Aliases[R];
138 sort(A, CompareByName);
139 return {R, std::move(A)};
142 std::function<Documentation(const Record *)> DocumentationForGroup =
143 [&](const Record *R) -> Documentation {
144 Documentation D;
146 auto &Groups = GroupsInGroup[R];
147 sort(Groups, CompareByLocation);
148 for (const Record *G : Groups) {
149 D.Groups.emplace_back();
150 D.Groups.back().Group = G;
151 Documentation &Base = D.Groups.back();
152 Base = DocumentationForGroup(G);
153 if (Base.empty())
154 D.Groups.pop_back();
157 auto &Options = OptionsInGroup[R];
158 sort(Options, CompareByName);
159 for (const Record *O : Options)
160 if (isOptionVisible(O, DocInfo))
161 D.Options.push_back(DocumentationForOption(O));
163 return D;
166 return DocumentationForGroup(nullptr);
169 // Get the first and successive separators to use for an OptionKind.
170 std::pair<StringRef,StringRef> getSeparatorsForKind(const Record *OptionKind) {
171 return StringSwitch<std::pair<StringRef, StringRef>>(OptionKind->getName())
172 .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE",
173 "KIND_JOINED_AND_SEPARATE",
174 "KIND_REMAINING_ARGS_JOINED", {"", " "})
175 .Case("KIND_COMMAJOINED", {"", ","})
176 .Default({" ", " "});
179 const unsigned UnlimitedArgs = unsigned(-1);
181 // Get the number of arguments expected for an option, or -1 if any number of
182 // arguments are accepted.
183 unsigned getNumArgsForKind(const Record *OptionKind, const Record *Option) {
184 return StringSwitch<unsigned>(OptionKind->getName())
185 .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE", "KIND_SEPARATE", 1)
186 .Cases("KIND_REMAINING_ARGS", "KIND_REMAINING_ARGS_JOINED",
187 "KIND_COMMAJOINED", UnlimitedArgs)
188 .Case("KIND_JOINED_AND_SEPARATE", 2)
189 .Case("KIND_MULTIARG", Option->getValueAsInt("NumArgs"))
190 .Default(0);
193 std::string escapeRST(StringRef Str) {
194 std::string Out;
195 for (auto K : Str) {
196 if (StringRef("`*|[]\\").count(K))
197 Out.push_back('\\');
198 Out.push_back(K);
200 return Out;
203 StringRef getSphinxOptionID(StringRef OptionName) {
204 for (auto I = OptionName.begin(), E = OptionName.end(); I != E; ++I)
205 if (!isalnum(*I) && *I != '-')
206 return OptionName.substr(0, I - OptionName.begin());
207 return OptionName;
210 bool canSphinxCopeWithOption(const Record *Option) {
211 // HACK: Work arond sphinx's inability to cope with punctuation-only options
212 // such as /? by suppressing them from the option list.
213 for (char C : Option->getValueAsString("Name"))
214 if (isalnum(C))
215 return true;
216 return false;
219 void emitHeading(int Depth, std::string Heading, raw_ostream &OS) {
220 assert(Depth < 8 && "groups nested too deeply");
221 OS << Heading << '\n'
222 << std::string(Heading.size(), "=~-_'+<>"[Depth]) << "\n";
225 /// Get the value of field \p Primary, if possible. If \p Primary does not
226 /// exist, get the value of \p Fallback and escape it for rST emission.
227 std::string getRSTStringWithTextFallback(const Record *R, StringRef Primary,
228 StringRef Fallback) {
229 for (auto Field : {Primary, Fallback}) {
230 if (auto *V = R->getValue(Field)) {
231 StringRef Value;
232 if (auto *SV = dyn_cast_or_null<StringInit>(V->getValue()))
233 Value = SV->getValue();
234 if (!Value.empty())
235 return Field == Primary ? Value.str() : escapeRST(Value);
238 return std::string(StringRef());
241 void emitOptionWithArgs(StringRef Prefix, const Record *Option,
242 ArrayRef<StringRef> Args, raw_ostream &OS) {
243 OS << Prefix << escapeRST(Option->getValueAsString("Name"));
245 std::pair<StringRef, StringRef> Separators =
246 getSeparatorsForKind(Option->getValueAsDef("Kind"));
248 StringRef Separator = Separators.first;
249 for (auto Arg : Args) {
250 OS << Separator << escapeRST(Arg);
251 Separator = Separators.second;
255 constexpr StringLiteral DefaultMetaVarName = "<arg>";
257 void emitOptionName(StringRef Prefix, const Record *Option, raw_ostream &OS) {
258 // Find the arguments to list after the option.
259 unsigned NumArgs = getNumArgsForKind(Option->getValueAsDef("Kind"), Option);
260 bool HasMetaVarName = !Option->isValueUnset("MetaVarName");
262 std::vector<std::string> Args;
263 if (HasMetaVarName)
264 Args.push_back(std::string(Option->getValueAsString("MetaVarName")));
265 else if (NumArgs == 1)
266 Args.push_back(DefaultMetaVarName.str());
268 // Fill up arguments if this option didn't provide a meta var name or it
269 // supports an unlimited number of arguments. We can't see how many arguments
270 // already are in a meta var name, so assume it has right number. This is
271 // needed for JoinedAndSeparate options so that there arent't too many
272 // arguments.
273 if (!HasMetaVarName || NumArgs == UnlimitedArgs) {
274 while (Args.size() < NumArgs) {
275 Args.push_back(("<arg" + Twine(Args.size() + 1) + ">").str());
276 // Use '--args <arg1> <arg2>...' if any number of args are allowed.
277 if (Args.size() == 2 && NumArgs == UnlimitedArgs) {
278 Args.back() += "...";
279 break;
284 emitOptionWithArgs(Prefix, Option,
285 std::vector<StringRef>(Args.begin(), Args.end()), OS);
287 auto AliasArgs = Option->getValueAsListOfStrings("AliasArgs");
288 if (!AliasArgs.empty()) {
289 const Record *Alias = Option->getValueAsDef("Alias");
290 OS << " (equivalent to ";
291 emitOptionWithArgs(
292 Alias->getValueAsListOfStrings("Prefixes").front(), Alias,
293 AliasArgs, OS);
294 OS << ")";
298 bool emitOptionNames(const Record *Option, raw_ostream &OS, bool EmittedAny) {
299 for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes")) {
300 if (EmittedAny)
301 OS << ", ";
302 emitOptionName(Prefix, Option, OS);
303 EmittedAny = true;
305 return EmittedAny;
308 template <typename Fn>
309 void forEachOptionName(const DocumentedOption &Option, const Record *DocInfo,
310 Fn F) {
311 F(Option.Option);
313 for (auto *Alias : Option.Aliases)
314 if (isOptionVisible(Alias, DocInfo) &&
315 canSphinxCopeWithOption(Option.Option))
316 F(Alias);
319 void emitOption(const DocumentedOption &Option, const Record *DocInfo,
320 raw_ostream &OS) {
321 if (Option.Option->getValueAsDef("Kind")->getName() == "KIND_UNKNOWN" ||
322 Option.Option->getValueAsDef("Kind")->getName() == "KIND_INPUT")
323 return;
324 if (!canSphinxCopeWithOption(Option.Option))
325 return;
327 // HACK: Emit a different program name with each option to work around
328 // sphinx's inability to cope with options that differ only by punctuation
329 // (eg -ObjC vs -ObjC++, -G vs -G=).
330 std::vector<std::string> SphinxOptionIDs;
331 forEachOptionName(Option, DocInfo, [&](const Record *Option) {
332 for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes"))
333 SphinxOptionIDs.push_back(std::string(getSphinxOptionID(
334 (Prefix + Option->getValueAsString("Name")).str())));
336 assert(!SphinxOptionIDs.empty() && "no flags for option");
337 static std::map<std::string, int> NextSuffix;
338 int SphinxWorkaroundSuffix = NextSuffix[*std::max_element(
339 SphinxOptionIDs.begin(), SphinxOptionIDs.end(),
340 [&](const std::string &A, const std::string &B) {
341 return NextSuffix[A] < NextSuffix[B];
342 })];
343 for (auto &S : SphinxOptionIDs)
344 NextSuffix[S] = SphinxWorkaroundSuffix + 1;
346 std::string Program = DocInfo->getValueAsString("Program").lower();
347 if (SphinxWorkaroundSuffix)
348 OS << ".. program:: " << Program << SphinxWorkaroundSuffix << "\n";
350 // Emit the names of the option.
351 OS << ".. option:: ";
352 bool EmittedAny = false;
353 forEachOptionName(Option, DocInfo, [&](const Record *Option) {
354 EmittedAny = emitOptionNames(Option, OS, EmittedAny);
356 if (SphinxWorkaroundSuffix)
357 OS << "\n.. program:: " << Program;
358 OS << "\n\n";
360 // Emit the description, if we have one.
361 const Record *R = Option.Option;
362 std::string Description;
364 // Prefer a program specific help string.
365 // This is a list of (visibilities, string) pairs.
366 for (const Record *VisibilityHelp :
367 R->getValueAsListOfDefs("HelpTextsForVariants")) {
368 // This is a list of visibilities.
369 ArrayRef<const Init *> Visibilities =
370 VisibilityHelp->getValueAsListInit("Visibilities")->getValues();
372 // See if any of the program's visibilities are in the list.
373 for (StringRef DocInfoMask :
374 DocInfo->getValueAsListOfStrings("VisibilityMask")) {
375 for (const Init *Visibility : Visibilities) {
376 if (Visibility->getAsUnquotedString() == DocInfoMask) {
377 // Use the first one we find.
378 Description = escapeRST(VisibilityHelp->getValueAsString("Text"));
379 break;
382 if (!Description.empty())
383 break;
386 if (!Description.empty())
387 break;
390 // If there's not a program specific string, use the default one.
391 if (Description.empty())
392 Description = getRSTStringWithTextFallback(R, "DocBrief", "HelpText");
394 if (!isa<UnsetInit>(R->getValueInit("Values"))) {
395 if (!Description.empty() && Description.back() != '.')
396 Description.push_back('.');
398 StringRef MetaVarName;
399 if (!isa<UnsetInit>(R->getValueInit("MetaVarName")))
400 MetaVarName = R->getValueAsString("MetaVarName");
401 else
402 MetaVarName = DefaultMetaVarName;
404 SmallVector<StringRef> Values;
405 SplitString(R->getValueAsString("Values"), Values, ",");
406 Description += (" " + MetaVarName + " must be '").str();
407 if (Values.size() > 1) {
408 Description += join(Values.begin(), Values.end() - 1, "', '");
409 Description += "' or '";
411 Description += (Values.back() + "'.").str();
414 if (!Description.empty())
415 OS << Description << "\n\n";
418 void emitDocumentation(int Depth, const Documentation &Doc,
419 const Record *DocInfo, raw_ostream &OS);
421 void emitGroup(int Depth, const DocumentedGroup &Group, const Record *DocInfo,
422 raw_ostream &OS) {
423 emitHeading(Depth,
424 getRSTStringWithTextFallback(Group.Group, "DocName", "Name"), OS);
426 // Emit the description, if we have one.
427 std::string Description =
428 getRSTStringWithTextFallback(Group.Group, "DocBrief", "HelpText");
429 if (!Description.empty())
430 OS << Description << "\n\n";
432 // Emit contained options and groups.
433 emitDocumentation(Depth + 1, Group, DocInfo, OS);
436 void emitDocumentation(int Depth, const Documentation &Doc,
437 const Record *DocInfo, raw_ostream &OS) {
438 for (auto &O : Doc.Options)
439 emitOption(O, DocInfo, OS);
440 for (auto &G : Doc.Groups)
441 emitGroup(Depth, G, DocInfo, OS);
444 } // namespace
446 void clang::EmitClangOptDocs(const RecordKeeper &Records, raw_ostream &OS) {
447 const Record *DocInfo = Records.getDef("GlobalDocumentation");
448 if (!DocInfo) {
449 PrintFatalError("The GlobalDocumentation top-level definition is missing, "
450 "no documentation will be generated.");
451 return;
453 OS << DocInfo->getValueAsString("Intro") << "\n";
454 OS << ".. program:: " << DocInfo->getValueAsString("Program").lower() << "\n";
456 emitDocumentation(0, extractDocumentation(Records, DocInfo), DocInfo, OS);