1d90443b1SValentin Clement //===- DirectiveEmitter.cpp - Directive Language Emitter ------------------===//
2d90443b1SValentin Clement //
3d90443b1SValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4d90443b1SValentin Clement // See https://llvm.org/LICENSE.txt for license information.
5d90443b1SValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d90443b1SValentin Clement //
7d90443b1SValentin Clement //===----------------------------------------------------------------------===//
8d90443b1SValentin Clement //
9d90443b1SValentin Clement // DirectiveEmitter uses the descriptions of directives and clauses to construct
10d90443b1SValentin Clement // common code declarations to be used in Frontends.
11d90443b1SValentin Clement //
12d90443b1SValentin Clement //===----------------------------------------------------------------------===//
13d90443b1SValentin Clement 
14e6c5e6efSKiran Chandramohan #include "llvm/TableGen/DirectiveEmitter.h"
15d90443b1SValentin Clement #include "llvm/ADT/STLExtras.h"
16d90443b1SValentin Clement #include "llvm/ADT/SmallVector.h"
176e42a417SValentin Clement #include "llvm/ADT/StringSet.h"
18048aaab1SValentin Clement #include "llvm/ADT/StringSwitch.h"
19d90443b1SValentin Clement #include "llvm/TableGen/Error.h"
20d90443b1SValentin Clement #include "llvm/TableGen/Record.h"
21d90443b1SValentin Clement 
221a70077bSValentin Clement using namespace llvm;
231a70077bSValentin Clement 
246e42a417SValentin Clement namespace {
256e42a417SValentin Clement // Simple RAII helper for defining ifdef-undef-endif scopes.
266e42a417SValentin Clement class IfDefScope {
276e42a417SValentin Clement public:
IfDefScope(StringRef Name,raw_ostream & OS)286e42a417SValentin Clement   IfDefScope(StringRef Name, raw_ostream &OS) : Name(Name), OS(OS) {
296e42a417SValentin Clement     OS << "#ifdef " << Name << "\n"
306e42a417SValentin Clement        << "#undef " << Name << "\n";
316e42a417SValentin Clement   }
326e42a417SValentin Clement 
~IfDefScope()336e42a417SValentin Clement   ~IfDefScope() { OS << "\n#endif // " << Name << "\n\n"; }
346e42a417SValentin Clement 
356e42a417SValentin Clement private:
366e42a417SValentin Clement   StringRef Name;
376e42a417SValentin Clement   raw_ostream &OS;
386e42a417SValentin Clement };
396e42a417SValentin Clement } // end anonymous namespace
406e42a417SValentin Clement 
41d90443b1SValentin Clement namespace llvm {
421a70077bSValentin Clement 
431a70077bSValentin Clement // Generate enum class
GenerateEnumClass(const std::vector<Record * > & Records,raw_ostream & OS,StringRef Enum,StringRef Prefix,const DirectiveLanguage & DirLang)441a70077bSValentin Clement void GenerateEnumClass(const std::vector<Record *> &Records, raw_ostream &OS,
45e8d4038eSValentin Clement                        StringRef Enum, StringRef Prefix,
466c337945SValentin Clement                        const DirectiveLanguage &DirLang) {
471a70077bSValentin Clement   OS << "\n";
481a70077bSValentin Clement   OS << "enum class " << Enum << " {\n";
491a70077bSValentin Clement   for (const auto &R : Records) {
50e8d4038eSValentin Clement     BaseRecord Rec{R};
51e8d4038eSValentin Clement     OS << "  " << Prefix << Rec.getFormattedName() << ",\n";
521a70077bSValentin Clement   }
531a70077bSValentin Clement   OS << "};\n";
541a70077bSValentin Clement   OS << "\n";
551a70077bSValentin Clement   OS << "static constexpr std::size_t " << Enum
561a70077bSValentin Clement      << "_enumSize = " << Records.size() << ";\n";
571a70077bSValentin Clement 
581a70077bSValentin Clement   // Make the enum values available in the defined namespace. This allows us to
591a70077bSValentin Clement   // write something like Enum_X if we have a `using namespace <CppNamespace>`.
601a70077bSValentin Clement   // At the same time we do not loose the strong type guarantees of the enum
611a70077bSValentin Clement   // class, that is we cannot pass an unsigned as Directive without an explicit
621a70077bSValentin Clement   // cast.
63e8d4038eSValentin Clement   if (DirLang.hasMakeEnumAvailableInNamespace()) {
641a70077bSValentin Clement     OS << "\n";
651a70077bSValentin Clement     for (const auto &R : Records) {
66e8d4038eSValentin Clement       BaseRecord Rec{R};
67e8d4038eSValentin Clement       OS << "constexpr auto " << Prefix << Rec.getFormattedName() << " = "
68e8d4038eSValentin Clement          << "llvm::" << DirLang.getCppNamespace() << "::" << Enum
69e8d4038eSValentin Clement          << "::" << Prefix << Rec.getFormattedName() << ";\n";
701a70077bSValentin Clement     }
711a70077bSValentin Clement   }
721a70077bSValentin Clement }
731a70077bSValentin Clement 
74e6c5e6efSKiran Chandramohan // Generate enums for values that clauses can take.
75e6c5e6efSKiran Chandramohan // Also generate function declarations for get<Enum>Name(StringRef Str).
GenerateEnumClauseVal(const std::vector<Record * > & Records,raw_ostream & OS,const DirectiveLanguage & DirLang,std::string & EnumHelperFuncs)76e6c5e6efSKiran Chandramohan void GenerateEnumClauseVal(const std::vector<Record *> &Records,
776c337945SValentin Clement                            raw_ostream &OS, const DirectiveLanguage &DirLang,
78e6c5e6efSKiran Chandramohan                            std::string &EnumHelperFuncs) {
79e6c5e6efSKiran Chandramohan   for (const auto &R : Records) {
80e6c5e6efSKiran Chandramohan     Clause C{R};
81e6c5e6efSKiran Chandramohan     const auto &ClauseVals = C.getClauseVals();
82e6c5e6efSKiran Chandramohan     if (ClauseVals.size() <= 0)
83e6c5e6efSKiran Chandramohan       continue;
84e6c5e6efSKiran Chandramohan 
85e6c5e6efSKiran Chandramohan     const auto &EnumName = C.getEnumName();
86e6c5e6efSKiran Chandramohan     if (EnumName.size() == 0) {
87e6c5e6efSKiran Chandramohan       PrintError("enumClauseValue field not set in Clause" +
88e6c5e6efSKiran Chandramohan                  C.getFormattedName() + ".");
89e6c5e6efSKiran Chandramohan       return;
90e6c5e6efSKiran Chandramohan     }
91e6c5e6efSKiran Chandramohan 
92e6c5e6efSKiran Chandramohan     OS << "\n";
93e6c5e6efSKiran Chandramohan     OS << "enum class " << EnumName << " {\n";
94e6c5e6efSKiran Chandramohan     for (const auto &CV : ClauseVals) {
95e6c5e6efSKiran Chandramohan       ClauseVal CVal{CV};
96e6c5e6efSKiran Chandramohan       OS << "  " << CV->getName() << "=" << CVal.getValue() << ",\n";
97e6c5e6efSKiran Chandramohan     }
98e6c5e6efSKiran Chandramohan     OS << "};\n";
99e6c5e6efSKiran Chandramohan 
100e6c5e6efSKiran Chandramohan     if (DirLang.hasMakeEnumAvailableInNamespace()) {
101e6c5e6efSKiran Chandramohan       OS << "\n";
102e6c5e6efSKiran Chandramohan       for (const auto &CV : ClauseVals) {
103e6c5e6efSKiran Chandramohan         OS << "constexpr auto " << CV->getName() << " = "
104e6c5e6efSKiran Chandramohan            << "llvm::" << DirLang.getCppNamespace() << "::" << EnumName
105e6c5e6efSKiran Chandramohan            << "::" << CV->getName() << ";\n";
106e6c5e6efSKiran Chandramohan       }
107e6c5e6efSKiran Chandramohan       EnumHelperFuncs += (llvm::Twine(EnumName) + llvm::Twine(" get") +
108e6c5e6efSKiran Chandramohan                           llvm::Twine(EnumName) + llvm::Twine("(StringRef);\n"))
109e6c5e6efSKiran Chandramohan                              .str();
1108f933a4eSValentin Clement 
1118f933a4eSValentin Clement       EnumHelperFuncs +=
1128f933a4eSValentin Clement           (llvm::Twine("llvm::StringRef get") + llvm::Twine(DirLang.getName()) +
1138f933a4eSValentin Clement            llvm::Twine(EnumName) + llvm::Twine("Name(") +
1148f933a4eSValentin Clement            llvm::Twine(EnumName) + llvm::Twine(");\n"))
1158f933a4eSValentin Clement               .str();
116e6c5e6efSKiran Chandramohan     }
117e6c5e6efSKiran Chandramohan   }
118e6c5e6efSKiran Chandramohan }
119e6c5e6efSKiran Chandramohan 
HasDuplicateClauses(const std::vector<Record * > & Clauses,const Directive & Directive,llvm::StringSet<> & CrtClauses)120c89b6457SValentin Clement bool HasDuplicateClauses(const std::vector<Record *> &Clauses,
121c89b6457SValentin Clement                          const Directive &Directive,
122c89b6457SValentin Clement                          llvm::StringSet<> &CrtClauses) {
1239914a873SValentin Clement   bool HasError = false;
124c89b6457SValentin Clement   for (const auto &C : Clauses) {
125c89b6457SValentin Clement     VersionedClause VerClause{C};
126c89b6457SValentin Clement     const auto insRes = CrtClauses.insert(VerClause.getClause().getName());
127c89b6457SValentin Clement     if (!insRes.second) {
128c89b6457SValentin Clement       PrintError("Clause " + VerClause.getClause().getRecordName() +
129c89b6457SValentin Clement                  " already defined on directive " + Directive.getRecordName());
1309914a873SValentin Clement       HasError = true;
131c89b6457SValentin Clement     }
132c89b6457SValentin Clement   }
1339914a873SValentin Clement   return HasError;
134c89b6457SValentin Clement }
135d90443b1SValentin Clement 
136a8a10acbSValentin Clement // Check for duplicate clauses in lists. Clauses cannot appear twice in the
137a8a10acbSValentin Clement // three allowed list. Also, since required implies allowed, clauses cannot
138a8a10acbSValentin Clement // appear in both the allowedClauses and requiredClauses lists.
HasDuplicateClausesInDirectives(const std::vector<Record * > & Directives)139c89b6457SValentin Clement bool HasDuplicateClausesInDirectives(const std::vector<Record *> &Directives) {
1409914a873SValentin Clement   bool HasDuplicate = false;
141c89b6457SValentin Clement   for (const auto &D : Directives) {
142c89b6457SValentin Clement     Directive Dir{D};
143c89b6457SValentin Clement     llvm::StringSet<> Clauses;
144a8a10acbSValentin Clement     // Check for duplicates in the three allowed lists.
145c89b6457SValentin Clement     if (HasDuplicateClauses(Dir.getAllowedClauses(), Dir, Clauses) ||
146c89b6457SValentin Clement         HasDuplicateClauses(Dir.getAllowedOnceClauses(), Dir, Clauses) ||
147a8a10acbSValentin Clement         HasDuplicateClauses(Dir.getAllowedExclusiveClauses(), Dir, Clauses)) {
1489914a873SValentin Clement       HasDuplicate = true;
149a8a10acbSValentin Clement     }
150a8a10acbSValentin Clement     // Check for duplicate between allowedClauses and required
151a8a10acbSValentin Clement     Clauses.clear();
152a8a10acbSValentin Clement     if (HasDuplicateClauses(Dir.getAllowedClauses(), Dir, Clauses) ||
153c89b6457SValentin Clement         HasDuplicateClauses(Dir.getRequiredClauses(), Dir, Clauses)) {
1549914a873SValentin Clement       HasDuplicate = true;
155a8a10acbSValentin Clement     }
1569914a873SValentin Clement     if (HasDuplicate)
157a8a10acbSValentin Clement       PrintFatalError("One or more clauses are defined multiple times on"
158a8a10acbSValentin Clement                       " directive " +
159c89b6457SValentin Clement                       Dir.getRecordName());
160c89b6457SValentin Clement   }
161a8a10acbSValentin Clement 
1629914a873SValentin Clement   return HasDuplicate;
163c89b6457SValentin Clement }
164c89b6457SValentin Clement 
165c89b6457SValentin Clement // Check consitency of records. Return true if an error has been detected.
166c89b6457SValentin Clement // Return false if the records are valid.
HasValidityErrors() const1679914a873SValentin Clement bool DirectiveLanguage::HasValidityErrors() const {
1686c337945SValentin Clement   if (getDirectiveLanguages().size() != 1) {
169a8a10acbSValentin Clement     PrintFatalError("A single definition of DirectiveLanguage is needed.");
170c89b6457SValentin Clement     return true;
171d90443b1SValentin Clement   }
172d90443b1SValentin Clement 
1736c337945SValentin Clement   return HasDuplicateClausesInDirectives(getDirectives());
174c89b6457SValentin Clement }
175c89b6457SValentin Clement 
176c89b6457SValentin Clement // Generate the declaration section for the enumeration in the directive
177c89b6457SValentin Clement // language
EmitDirectivesDecl(RecordKeeper & Records,raw_ostream & OS)178c89b6457SValentin Clement void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) {
1796c337945SValentin Clement   const auto DirLang = DirectiveLanguage{Records};
1809914a873SValentin Clement   if (DirLang.HasValidityErrors())
181c89b6457SValentin Clement     return;
182c89b6457SValentin Clement 
183e8d4038eSValentin Clement   OS << "#ifndef LLVM_" << DirLang.getName() << "_INC\n";
184e8d4038eSValentin Clement   OS << "#define LLVM_" << DirLang.getName() << "_INC\n";
185d90443b1SValentin Clement 
186e8d4038eSValentin Clement   if (DirLang.hasEnableBitmaskEnumInNamespace())
1871a70077bSValentin Clement     OS << "\n#include \"llvm/ADT/BitmaskEnum.h\"\n";
188d90443b1SValentin Clement 
1891a70077bSValentin Clement   OS << "\n";
190d90443b1SValentin Clement   OS << "namespace llvm {\n";
1911a70077bSValentin Clement   OS << "class StringRef;\n";
192d90443b1SValentin Clement 
193d90443b1SValentin Clement   // Open namespaces defined in the directive language
194d90443b1SValentin Clement   llvm::SmallVector<StringRef, 2> Namespaces;
195e8d4038eSValentin Clement   llvm::SplitString(DirLang.getCppNamespace(), Namespaces, "::");
196d90443b1SValentin Clement   for (auto Ns : Namespaces)
197d90443b1SValentin Clement     OS << "namespace " << Ns << " {\n";
198d90443b1SValentin Clement 
199e8d4038eSValentin Clement   if (DirLang.hasEnableBitmaskEnumInNamespace())
2001a70077bSValentin Clement     OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n";
201d90443b1SValentin Clement 
202d90443b1SValentin Clement   // Emit Directive enumeration
2036c337945SValentin Clement   GenerateEnumClass(DirLang.getDirectives(), OS, "Directive",
2046c337945SValentin Clement                     DirLang.getDirectivePrefix(), DirLang);
205d90443b1SValentin Clement 
206d90443b1SValentin Clement   // Emit Clause enumeration
2076c337945SValentin Clement   GenerateEnumClass(DirLang.getClauses(), OS, "Clause",
2086c337945SValentin Clement                     DirLang.getClausePrefix(), DirLang);
209d90443b1SValentin Clement 
210e6c5e6efSKiran Chandramohan   // Emit ClauseVal enumeration
211e6c5e6efSKiran Chandramohan   std::string EnumHelperFuncs;
2126c337945SValentin Clement   GenerateEnumClauseVal(DirLang.getClauses(), OS, DirLang, EnumHelperFuncs);
213e6c5e6efSKiran Chandramohan 
2141a70077bSValentin Clement   // Generic function signatures
2151a70077bSValentin Clement   OS << "\n";
2161a70077bSValentin Clement   OS << "// Enumeration helper functions\n";
217e8d4038eSValentin Clement   OS << "Directive get" << DirLang.getName()
2181a70077bSValentin Clement      << "DirectiveKind(llvm::StringRef Str);\n";
2191a70077bSValentin Clement   OS << "\n";
220e8d4038eSValentin Clement   OS << "llvm::StringRef get" << DirLang.getName()
2211a70077bSValentin Clement      << "DirectiveName(Directive D);\n";
2221a70077bSValentin Clement   OS << "\n";
223e8d4038eSValentin Clement   OS << "Clause get" << DirLang.getName()
224e8d4038eSValentin Clement      << "ClauseKind(llvm::StringRef Str);\n";
2251a70077bSValentin Clement   OS << "\n";
226e8d4038eSValentin Clement   OS << "llvm::StringRef get" << DirLang.getName() << "ClauseName(Clause C);\n";
2271a70077bSValentin Clement   OS << "\n";
22865482e8aSValentin Clement   OS << "/// Return true if \\p C is a valid clause for \\p D in version \\p "
22965482e8aSValentin Clement      << "Version.\n";
23065482e8aSValentin Clement   OS << "bool isAllowedClauseForDirective(Directive D, "
23165482e8aSValentin Clement      << "Clause C, unsigned Version);\n";
23265482e8aSValentin Clement   OS << "\n";
233e6c5e6efSKiran Chandramohan   if (EnumHelperFuncs.length() > 0) {
234e6c5e6efSKiran Chandramohan     OS << EnumHelperFuncs;
235e6c5e6efSKiran Chandramohan     OS << "\n";
236e6c5e6efSKiran Chandramohan   }
237d90443b1SValentin Clement 
238d90443b1SValentin Clement   // Closing namespaces
239d90443b1SValentin Clement   for (auto Ns : llvm::reverse(Namespaces))
240d90443b1SValentin Clement     OS << "} // namespace " << Ns << "\n";
241d90443b1SValentin Clement 
242d90443b1SValentin Clement   OS << "} // namespace llvm\n";
243d90443b1SValentin Clement 
244e8d4038eSValentin Clement   OS << "#endif // LLVM_" << DirLang.getName() << "_INC\n";
245d90443b1SValentin Clement }
2461a70077bSValentin Clement 
2471a70077bSValentin Clement // Generate function implementation for get<Enum>Name(StringRef Str)
GenerateGetName(const std::vector<Record * > & Records,raw_ostream & OS,StringRef Enum,const DirectiveLanguage & DirLang,StringRef Prefix)2481a70077bSValentin Clement void GenerateGetName(const std::vector<Record *> &Records, raw_ostream &OS,
2496c337945SValentin Clement                      StringRef Enum, const DirectiveLanguage &DirLang,
250e8d4038eSValentin Clement                      StringRef Prefix) {
2511a70077bSValentin Clement   OS << "\n";
252e8d4038eSValentin Clement   OS << "llvm::StringRef llvm::" << DirLang.getCppNamespace() << "::get"
253e8d4038eSValentin Clement      << DirLang.getName() << Enum << "Name(" << Enum << " Kind) {\n";
2541a70077bSValentin Clement   OS << "  switch (Kind) {\n";
2551a70077bSValentin Clement   for (const auto &R : Records) {
256e8d4038eSValentin Clement     BaseRecord Rec{R};
257e8d4038eSValentin Clement     OS << "    case " << Prefix << Rec.getFormattedName() << ":\n";
2581a70077bSValentin Clement     OS << "      return \"";
259e8d4038eSValentin Clement     if (Rec.getAlternativeName().empty())
260e8d4038eSValentin Clement       OS << Rec.getName();
2611a70077bSValentin Clement     else
262e8d4038eSValentin Clement       OS << Rec.getAlternativeName();
2631a70077bSValentin Clement     OS << "\";\n";
2641a70077bSValentin Clement   }
2651a70077bSValentin Clement   OS << "  }\n"; // switch
266e8d4038eSValentin Clement   OS << "  llvm_unreachable(\"Invalid " << DirLang.getName() << " " << Enum
2671a70077bSValentin Clement      << " kind\");\n";
2681a70077bSValentin Clement   OS << "}\n";
2691a70077bSValentin Clement }
2701a70077bSValentin Clement 
2711a70077bSValentin Clement // Generate function implementation for get<Enum>Kind(StringRef Str)
GenerateGetKind(const std::vector<Record * > & Records,raw_ostream & OS,StringRef Enum,const DirectiveLanguage & DirLang,StringRef Prefix,bool ImplicitAsUnknown)2721a70077bSValentin Clement void GenerateGetKind(const std::vector<Record *> &Records, raw_ostream &OS,
2736c337945SValentin Clement                      StringRef Enum, const DirectiveLanguage &DirLang,
274e8d4038eSValentin Clement                      StringRef Prefix, bool ImplicitAsUnknown) {
2751a70077bSValentin Clement 
2761c5b8482SKazu Hirata   auto DefaultIt = llvm::find_if(
2771c5b8482SKazu Hirata       Records, [](Record *R) { return R->getValueAsBit("isDefault") == true; });
2781a70077bSValentin Clement 
2791a70077bSValentin Clement   if (DefaultIt == Records.end()) {
280e6c5e6efSKiran Chandramohan     PrintError("At least one " + Enum + " must be defined as default.");
2811a70077bSValentin Clement     return;
2821a70077bSValentin Clement   }
2831a70077bSValentin Clement 
284e8d4038eSValentin Clement   BaseRecord DefaultRec{(*DefaultIt)};
2851a70077bSValentin Clement 
2861a70077bSValentin Clement   OS << "\n";
287e8d4038eSValentin Clement   OS << Enum << " llvm::" << DirLang.getCppNamespace() << "::get"
288e8d4038eSValentin Clement      << DirLang.getName() << Enum << "Kind(llvm::StringRef Str) {\n";
2891a70077bSValentin Clement   OS << "  return llvm::StringSwitch<" << Enum << ">(Str)\n";
2901a70077bSValentin Clement 
2911a70077bSValentin Clement   for (const auto &R : Records) {
292e8d4038eSValentin Clement     BaseRecord Rec{R};
2931a70077bSValentin Clement     if (ImplicitAsUnknown && R->getValueAsBit("isImplicit")) {
294e8d4038eSValentin Clement       OS << "    .Case(\"" << Rec.getName() << "\"," << Prefix
295e8d4038eSValentin Clement          << DefaultRec.getFormattedName() << ")\n";
2961a70077bSValentin Clement     } else {
297e8d4038eSValentin Clement       OS << "    .Case(\"" << Rec.getName() << "\"," << Prefix
298e8d4038eSValentin Clement          << Rec.getFormattedName() << ")\n";
2991a70077bSValentin Clement     }
3001a70077bSValentin Clement   }
301e8d4038eSValentin Clement   OS << "    .Default(" << Prefix << DefaultRec.getFormattedName() << ");\n";
30265482e8aSValentin Clement   OS << "}\n";
30365482e8aSValentin Clement }
30465482e8aSValentin Clement 
305e6c5e6efSKiran Chandramohan // Generate function implementation for get<ClauseVal>Kind(StringRef Str)
GenerateGetKindClauseVal(const DirectiveLanguage & DirLang,raw_ostream & OS)3066c337945SValentin Clement void GenerateGetKindClauseVal(const DirectiveLanguage &DirLang,
3076c337945SValentin Clement                               raw_ostream &OS) {
3086c337945SValentin Clement   for (const auto &R : DirLang.getClauses()) {
309e6c5e6efSKiran Chandramohan     Clause C{R};
310e6c5e6efSKiran Chandramohan     const auto &ClauseVals = C.getClauseVals();
311e6c5e6efSKiran Chandramohan     if (ClauseVals.size() <= 0)
312e6c5e6efSKiran Chandramohan       continue;
313e6c5e6efSKiran Chandramohan 
3141c5b8482SKazu Hirata     auto DefaultIt = llvm::find_if(ClauseVals, [](Record *CV) {
315e6c5e6efSKiran Chandramohan       return CV->getValueAsBit("isDefault") == true;
316e6c5e6efSKiran Chandramohan     });
317e6c5e6efSKiran Chandramohan 
318e6c5e6efSKiran Chandramohan     if (DefaultIt == ClauseVals.end()) {
319e6c5e6efSKiran Chandramohan       PrintError("At least one val in Clause " + C.getFormattedName() +
320e6c5e6efSKiran Chandramohan                  " must be defined as default.");
321e6c5e6efSKiran Chandramohan       return;
322e6c5e6efSKiran Chandramohan     }
323e6c5e6efSKiran Chandramohan     const auto DefaultName = (*DefaultIt)->getName();
324e6c5e6efSKiran Chandramohan 
325e6c5e6efSKiran Chandramohan     const auto &EnumName = C.getEnumName();
326e6c5e6efSKiran Chandramohan     if (EnumName.size() == 0) {
327e6c5e6efSKiran Chandramohan       PrintError("enumClauseValue field not set in Clause" +
328e6c5e6efSKiran Chandramohan                  C.getFormattedName() + ".");
329e6c5e6efSKiran Chandramohan       return;
330e6c5e6efSKiran Chandramohan     }
331e6c5e6efSKiran Chandramohan 
332e6c5e6efSKiran Chandramohan     OS << "\n";
3336c337945SValentin Clement     OS << EnumName << " llvm::" << DirLang.getCppNamespace() << "::get"
3346c337945SValentin Clement        << EnumName << "(llvm::StringRef Str) {\n";
335e6c5e6efSKiran Chandramohan     OS << "  return llvm::StringSwitch<" << EnumName << ">(Str)\n";
336e6c5e6efSKiran Chandramohan     for (const auto &CV : ClauseVals) {
337e6c5e6efSKiran Chandramohan       ClauseVal CVal{CV};
338e6c5e6efSKiran Chandramohan       OS << "    .Case(\"" << CVal.getFormattedName() << "\"," << CV->getName()
339e6c5e6efSKiran Chandramohan          << ")\n";
340e6c5e6efSKiran Chandramohan     }
341e6c5e6efSKiran Chandramohan     OS << "    .Default(" << DefaultName << ");\n";
342e6c5e6efSKiran Chandramohan     OS << "}\n";
3438f933a4eSValentin Clement 
3448f933a4eSValentin Clement     OS << "\n";
3458f933a4eSValentin Clement     OS << "llvm::StringRef llvm::" << DirLang.getCppNamespace() << "::get"
3468f933a4eSValentin Clement        << DirLang.getName() << EnumName
3478f933a4eSValentin Clement        << "Name(llvm::" << DirLang.getCppNamespace() << "::" << EnumName
3488f933a4eSValentin Clement        << " x) {\n";
3498f933a4eSValentin Clement     OS << "  switch (x) {\n";
3508f933a4eSValentin Clement     for (const auto &CV : ClauseVals) {
3518f933a4eSValentin Clement       ClauseVal CVal{CV};
3528f933a4eSValentin Clement       OS << "    case " << CV->getName() << ":\n";
3538f933a4eSValentin Clement       OS << "      return \"" << CVal.getFormattedName() << "\";\n";
3548f933a4eSValentin Clement     }
3558f933a4eSValentin Clement     OS << "  }\n"; // switch
3568f933a4eSValentin Clement     OS << "  llvm_unreachable(\"Invalid " << DirLang.getName() << " "
3578f933a4eSValentin Clement        << EnumName << " kind\");\n";
3588f933a4eSValentin Clement     OS << "}\n";
359e6c5e6efSKiran Chandramohan   }
360e6c5e6efSKiran Chandramohan }
361e6c5e6efSKiran Chandramohan 
GenerateCaseForVersionedClauses(const std::vector<Record * > & Clauses,raw_ostream & OS,StringRef DirectiveName,const DirectiveLanguage & DirLang,llvm::StringSet<> & Cases)36223084878SValentin Clement void GenerateCaseForVersionedClauses(const std::vector<Record *> &Clauses,
36365482e8aSValentin Clement                                      raw_ostream &OS, StringRef DirectiveName,
3646c337945SValentin Clement                                      const DirectiveLanguage &DirLang,
3656e42a417SValentin Clement                                      llvm::StringSet<> &Cases) {
36665482e8aSValentin Clement   for (const auto &C : Clauses) {
367e8d4038eSValentin Clement     VersionedClause VerClause{C};
3686e42a417SValentin Clement 
369e8d4038eSValentin Clement     const auto ClauseFormattedName = VerClause.getClause().getFormattedName();
370e8d4038eSValentin Clement 
371437f9600SKazu Hirata     if (Cases.insert(ClauseFormattedName).second) {
372e8d4038eSValentin Clement       OS << "        case " << DirLang.getClausePrefix() << ClauseFormattedName
373e8d4038eSValentin Clement          << ":\n";
374e8d4038eSValentin Clement       OS << "          return " << VerClause.getMinVersion()
375e8d4038eSValentin Clement          << " <= Version && " << VerClause.getMaxVersion() << " >= Version;\n";
37665482e8aSValentin Clement     }
37765482e8aSValentin Clement   }
3786e42a417SValentin Clement }
37965482e8aSValentin Clement 
38065482e8aSValentin Clement // Generate the isAllowedClauseForDirective function implementation.
GenerateIsAllowedClause(const DirectiveLanguage & DirLang,raw_ostream & OS)3816c337945SValentin Clement void GenerateIsAllowedClause(const DirectiveLanguage &DirLang,
3826c337945SValentin Clement                              raw_ostream &OS) {
38365482e8aSValentin Clement   OS << "\n";
384e8d4038eSValentin Clement   OS << "bool llvm::" << DirLang.getCppNamespace()
385e8d4038eSValentin Clement      << "::isAllowedClauseForDirective("
38665482e8aSValentin Clement      << "Directive D, Clause C, unsigned Version) {\n";
387e8d4038eSValentin Clement   OS << "  assert(unsigned(D) <= llvm::" << DirLang.getCppNamespace()
38865482e8aSValentin Clement      << "::Directive_enumSize);\n";
389e8d4038eSValentin Clement   OS << "  assert(unsigned(C) <= llvm::" << DirLang.getCppNamespace()
39065482e8aSValentin Clement      << "::Clause_enumSize);\n";
39165482e8aSValentin Clement 
39223084878SValentin Clement   OS << "  switch (D) {\n";
39323084878SValentin Clement 
3946c337945SValentin Clement   for (const auto &D : DirLang.getDirectives()) {
395e8d4038eSValentin Clement     Directive Dir{D};
39623084878SValentin Clement 
397e8d4038eSValentin Clement     OS << "    case " << DirLang.getDirectivePrefix() << Dir.getFormattedName()
39823084878SValentin Clement        << ":\n";
399e8d4038eSValentin Clement     if (Dir.getAllowedClauses().size() == 0 &&
400e8d4038eSValentin Clement         Dir.getAllowedOnceClauses().size() == 0 &&
401e8d4038eSValentin Clement         Dir.getAllowedExclusiveClauses().size() == 0 &&
402e8d4038eSValentin Clement         Dir.getRequiredClauses().size() == 0) {
4037b67bc16SValentin Clement       OS << "      return false;\n";
4047b67bc16SValentin Clement     } else {
40523084878SValentin Clement       OS << "      switch (C) {\n";
40623084878SValentin Clement 
4076e42a417SValentin Clement       llvm::StringSet<> Cases;
4086e42a417SValentin Clement 
409e8d4038eSValentin Clement       GenerateCaseForVersionedClauses(Dir.getAllowedClauses(), OS,
410e8d4038eSValentin Clement                                       Dir.getName(), DirLang, Cases);
41165482e8aSValentin Clement 
412e8d4038eSValentin Clement       GenerateCaseForVersionedClauses(Dir.getAllowedOnceClauses(), OS,
413e8d4038eSValentin Clement                                       Dir.getName(), DirLang, Cases);
4146e42a417SValentin Clement 
415e8d4038eSValentin Clement       GenerateCaseForVersionedClauses(Dir.getAllowedExclusiveClauses(), OS,
416e8d4038eSValentin Clement                                       Dir.getName(), DirLang, Cases);
41765482e8aSValentin Clement 
418e8d4038eSValentin Clement       GenerateCaseForVersionedClauses(Dir.getRequiredClauses(), OS,
419e8d4038eSValentin Clement                                       Dir.getName(), DirLang, Cases);
42023084878SValentin Clement 
42123084878SValentin Clement       OS << "        default:\n";
42265482e8aSValentin Clement       OS << "          return false;\n";
42323084878SValentin Clement       OS << "      }\n"; // End of clauses switch
4247b67bc16SValentin Clement     }
42523084878SValentin Clement     OS << "      break;\n";
42623084878SValentin Clement   }
42723084878SValentin Clement 
42823084878SValentin Clement   OS << "  }\n"; // End of directives switch
429e8d4038eSValentin Clement   OS << "  llvm_unreachable(\"Invalid " << DirLang.getName()
43023084878SValentin Clement      << " Directive kind\");\n";
43123084878SValentin Clement   OS << "}\n"; // End of function isAllowedClauseForDirective
4321a70077bSValentin Clement }
4331a70077bSValentin Clement 
4346e42a417SValentin Clement // Generate a simple enum set with the give clauses.
GenerateClauseSet(const std::vector<Record * > & Clauses,raw_ostream & OS,StringRef ClauseSetPrefix,Directive & Dir,const DirectiveLanguage & DirLang)4356e42a417SValentin Clement void GenerateClauseSet(const std::vector<Record *> &Clauses, raw_ostream &OS,
436e8d4038eSValentin Clement                        StringRef ClauseSetPrefix, Directive &Dir,
4376c337945SValentin Clement                        const DirectiveLanguage &DirLang) {
4386e42a417SValentin Clement 
4396e42a417SValentin Clement   OS << "\n";
440e8d4038eSValentin Clement   OS << "  static " << DirLang.getClauseEnumSetClass() << " " << ClauseSetPrefix
441e8d4038eSValentin Clement      << DirLang.getDirectivePrefix() << Dir.getFormattedName() << " {\n";
4426e42a417SValentin Clement 
4436e42a417SValentin Clement   for (const auto &C : Clauses) {
444e8d4038eSValentin Clement     VersionedClause VerClause{C};
445e8d4038eSValentin Clement     OS << "    llvm::" << DirLang.getCppNamespace()
446e8d4038eSValentin Clement        << "::Clause::" << DirLang.getClausePrefix()
447e8d4038eSValentin Clement        << VerClause.getClause().getFormattedName() << ",\n";
4486e42a417SValentin Clement   }
4496e42a417SValentin Clement   OS << "  };\n";
4506e42a417SValentin Clement }
4516e42a417SValentin Clement 
4526e42a417SValentin Clement // Generate an enum set for the 4 kinds of clauses linked to a directive.
GenerateDirectiveClauseSets(const DirectiveLanguage & DirLang,raw_ostream & OS)4536c337945SValentin Clement void GenerateDirectiveClauseSets(const DirectiveLanguage &DirLang,
4546c337945SValentin Clement                                  raw_ostream &OS) {
4556e42a417SValentin Clement 
4566e42a417SValentin Clement   IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_SETS", OS);
4576e42a417SValentin Clement 
4586e42a417SValentin Clement   OS << "\n";
4596e42a417SValentin Clement   OS << "namespace llvm {\n";
4606e42a417SValentin Clement 
4616e42a417SValentin Clement   // Open namespaces defined in the directive language.
4626e42a417SValentin Clement   llvm::SmallVector<StringRef, 2> Namespaces;
463e8d4038eSValentin Clement   llvm::SplitString(DirLang.getCppNamespace(), Namespaces, "::");
4646e42a417SValentin Clement   for (auto Ns : Namespaces)
4656e42a417SValentin Clement     OS << "namespace " << Ns << " {\n";
4666e42a417SValentin Clement 
4676c337945SValentin Clement   for (const auto &D : DirLang.getDirectives()) {
468e8d4038eSValentin Clement     Directive Dir{D};
4696e42a417SValentin Clement 
4706e42a417SValentin Clement     OS << "\n";
471e8d4038eSValentin Clement     OS << "  // Sets for " << Dir.getName() << "\n";
4726e42a417SValentin Clement 
473e8d4038eSValentin Clement     GenerateClauseSet(Dir.getAllowedClauses(), OS, "allowedClauses_", Dir,
474e8d4038eSValentin Clement                       DirLang);
475e8d4038eSValentin Clement     GenerateClauseSet(Dir.getAllowedOnceClauses(), OS, "allowedOnceClauses_",
476e8d4038eSValentin Clement                       Dir, DirLang);
477e8d4038eSValentin Clement     GenerateClauseSet(Dir.getAllowedExclusiveClauses(), OS,
478e8d4038eSValentin Clement                       "allowedExclusiveClauses_", Dir, DirLang);
479e8d4038eSValentin Clement     GenerateClauseSet(Dir.getRequiredClauses(), OS, "requiredClauses_", Dir,
480e8d4038eSValentin Clement                       DirLang);
4816e42a417SValentin Clement   }
4826e42a417SValentin Clement 
4836e42a417SValentin Clement   // Closing namespaces
4846e42a417SValentin Clement   for (auto Ns : llvm::reverse(Namespaces))
4856e42a417SValentin Clement     OS << "} // namespace " << Ns << "\n";
4866e42a417SValentin Clement 
4876e42a417SValentin Clement   OS << "} // namespace llvm\n";
4886e42a417SValentin Clement }
4896e42a417SValentin Clement 
4906e42a417SValentin Clement // Generate a map of directive (key) with DirectiveClauses struct as values.
4916e42a417SValentin Clement // The struct holds the 4 sets of enumeration for the 4 kinds of clauses
4926e42a417SValentin Clement // allowances (allowed, allowed once, allowed exclusive and required).
GenerateDirectiveClauseMap(const DirectiveLanguage & DirLang,raw_ostream & OS)4936c337945SValentin Clement void GenerateDirectiveClauseMap(const DirectiveLanguage &DirLang,
4946c337945SValentin Clement                                 raw_ostream &OS) {
4956e42a417SValentin Clement 
4966e42a417SValentin Clement   IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_MAP", OS);
4976e42a417SValentin Clement 
4986e42a417SValentin Clement   OS << "\n";
49940626184SValentin Clement   OS << "{\n";
5006e42a417SValentin Clement 
5016c337945SValentin Clement   for (const auto &D : DirLang.getDirectives()) {
502e8d4038eSValentin Clement     Directive Dir{D};
503e8d4038eSValentin Clement     OS << "  {llvm::" << DirLang.getCppNamespace()
504e8d4038eSValentin Clement        << "::Directive::" << DirLang.getDirectivePrefix()
505e8d4038eSValentin Clement        << Dir.getFormattedName() << ",\n";
5066e42a417SValentin Clement     OS << "    {\n";
507e8d4038eSValentin Clement     OS << "      llvm::" << DirLang.getCppNamespace() << "::allowedClauses_"
508e8d4038eSValentin Clement        << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n";
509e8d4038eSValentin Clement     OS << "      llvm::" << DirLang.getCppNamespace() << "::allowedOnceClauses_"
510e8d4038eSValentin Clement        << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n";
511e8d4038eSValentin Clement     OS << "      llvm::" << DirLang.getCppNamespace()
512e8d4038eSValentin Clement        << "::allowedExclusiveClauses_" << DirLang.getDirectivePrefix()
513e8d4038eSValentin Clement        << Dir.getFormattedName() << ",\n";
514e8d4038eSValentin Clement     OS << "      llvm::" << DirLang.getCppNamespace() << "::requiredClauses_"
515e8d4038eSValentin Clement        << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n";
5166e42a417SValentin Clement     OS << "    }\n";
5176e42a417SValentin Clement     OS << "  },\n";
5186e42a417SValentin Clement   }
5196e42a417SValentin Clement 
52040626184SValentin Clement   OS << "}\n";
5216e42a417SValentin Clement }
5226e42a417SValentin Clement 
52316c1d251SValentin Clement // Generate classes entry for Flang clauses in the Flang parse-tree
52416c1d251SValentin Clement // If the clause as a non-generic class, no entry is generated.
52516c1d251SValentin Clement // If the clause does not hold a value, an EMPTY_CLASS is used.
52616c1d251SValentin Clement // If the clause class is generic then a WRAPPER_CLASS is used. When the value
52716c1d251SValentin Clement // is optional, the value class is wrapped into a std::optional.
GenerateFlangClauseParserClass(const DirectiveLanguage & DirLang,raw_ostream & OS)5286c337945SValentin Clement void GenerateFlangClauseParserClass(const DirectiveLanguage &DirLang,
52916c1d251SValentin Clement                                     raw_ostream &OS) {
53016c1d251SValentin Clement 
53116c1d251SValentin Clement   IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_CLASSES", OS);
53216c1d251SValentin Clement 
53316c1d251SValentin Clement   OS << "\n";
53416c1d251SValentin Clement 
5356c337945SValentin Clement   for (const auto &C : DirLang.getClauses()) {
53616c1d251SValentin Clement     Clause Clause{C};
5376bd0a445SValentin Clement     if (!Clause.getFlangClass().empty()) {
5383060894bSValentin Clement       OS << "WRAPPER_CLASS(" << Clause.getFormattedParserClassName() << ", ";
5393060894bSValentin Clement       if (Clause.isValueOptional() && Clause.isValueList()) {
5406bd0a445SValentin Clement         OS << "std::optional<std::list<" << Clause.getFlangClass() << ">>";
5413060894bSValentin Clement       } else if (Clause.isValueOptional()) {
5426bd0a445SValentin Clement         OS << "std::optional<" << Clause.getFlangClass() << ">";
5433060894bSValentin Clement       } else if (Clause.isValueList()) {
5446bd0a445SValentin Clement         OS << "std::list<" << Clause.getFlangClass() << ">";
54516c1d251SValentin Clement       } else {
5466bd0a445SValentin Clement         OS << Clause.getFlangClass();
54716c1d251SValentin Clement       }
54816c1d251SValentin Clement     } else {
5493060894bSValentin Clement       OS << "EMPTY_CLASS(" << Clause.getFormattedParserClassName();
55016c1d251SValentin Clement     }
5513060894bSValentin Clement     OS << ");\n";
55216c1d251SValentin Clement   }
55316c1d251SValentin Clement }
55416c1d251SValentin Clement 
55516c1d251SValentin Clement // Generate a list of the different clause classes for Flang.
GenerateFlangClauseParserClassList(const DirectiveLanguage & DirLang,raw_ostream & OS)5566c337945SValentin Clement void GenerateFlangClauseParserClassList(const DirectiveLanguage &DirLang,
55716c1d251SValentin Clement                                         raw_ostream &OS) {
55816c1d251SValentin Clement 
55916c1d251SValentin Clement   IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_CLASSES_LIST", OS);
56016c1d251SValentin Clement 
56116c1d251SValentin Clement   OS << "\n";
5626c337945SValentin Clement   llvm::interleaveComma(DirLang.getClauses(), OS, [&](Record *C) {
56316c1d251SValentin Clement     Clause Clause{C};
56416c1d251SValentin Clement     OS << Clause.getFormattedParserClassName() << "\n";
56516c1d251SValentin Clement   });
56616c1d251SValentin Clement }
56716c1d251SValentin Clement 
56816c1d251SValentin Clement // Generate dump node list for the clauses holding a generic class name.
GenerateFlangClauseDump(const DirectiveLanguage & DirLang,raw_ostream & OS)5696c337945SValentin Clement void GenerateFlangClauseDump(const DirectiveLanguage &DirLang,
57016c1d251SValentin Clement                              raw_ostream &OS) {
57116c1d251SValentin Clement 
57216c1d251SValentin Clement   IfDefScope Scope("GEN_FLANG_DUMP_PARSE_TREE_CLAUSES", OS);
57316c1d251SValentin Clement 
57416c1d251SValentin Clement   OS << "\n";
5756c337945SValentin Clement   for (const auto &C : DirLang.getClauses()) {
57616c1d251SValentin Clement     Clause Clause{C};
57716c1d251SValentin Clement     OS << "NODE(" << DirLang.getFlangClauseBaseClass() << ", "
57816c1d251SValentin Clement        << Clause.getFormattedParserClassName() << ")\n";
57916c1d251SValentin Clement   }
58016c1d251SValentin Clement }
58116c1d251SValentin Clement 
5823060894bSValentin Clement // Generate Unparse functions for clauses classes in the Flang parse-tree
5833060894bSValentin Clement // If the clause is a non-generic class, no entry is generated.
GenerateFlangClauseUnparse(const DirectiveLanguage & DirLang,raw_ostream & OS)5846c337945SValentin Clement void GenerateFlangClauseUnparse(const DirectiveLanguage &DirLang,
5853060894bSValentin Clement                                 raw_ostream &OS) {
5863060894bSValentin Clement 
5873060894bSValentin Clement   IfDefScope Scope("GEN_FLANG_CLAUSE_UNPARSE", OS);
5883060894bSValentin Clement 
5893060894bSValentin Clement   OS << "\n";
5903060894bSValentin Clement 
5916c337945SValentin Clement   for (const auto &C : DirLang.getClauses()) {
5923060894bSValentin Clement     Clause Clause{C};
5936bd0a445SValentin Clement     if (!Clause.getFlangClass().empty()) {
5943060894bSValentin Clement       if (Clause.isValueOptional() && Clause.getDefaultValue().empty()) {
5953060894bSValentin Clement         OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass()
5963060894bSValentin Clement            << "::" << Clause.getFormattedParserClassName() << " &x) {\n";
5973060894bSValentin Clement         OS << "  Word(\"" << Clause.getName().upper() << "\");\n";
5983060894bSValentin Clement 
5993060894bSValentin Clement         OS << "  Walk(\"(\", x.v, \")\");\n";
6003060894bSValentin Clement         OS << "}\n";
6013060894bSValentin Clement       } else if (Clause.isValueOptional()) {
6023060894bSValentin Clement         OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass()
6033060894bSValentin Clement            << "::" << Clause.getFormattedParserClassName() << " &x) {\n";
6043060894bSValentin Clement         OS << "  Word(\"" << Clause.getName().upper() << "\");\n";
6053060894bSValentin Clement         OS << "  Put(\"(\");\n";
6063060894bSValentin Clement         OS << "  if (x.v.has_value())\n";
6073060894bSValentin Clement         if (Clause.isValueList())
6083060894bSValentin Clement           OS << "    Walk(x.v, \",\");\n";
6093060894bSValentin Clement         else
6103060894bSValentin Clement           OS << "    Walk(x.v);\n";
6113060894bSValentin Clement         OS << "  else\n";
6123060894bSValentin Clement         OS << "    Put(\"" << Clause.getDefaultValue() << "\");\n";
6133060894bSValentin Clement         OS << "  Put(\")\");\n";
6143060894bSValentin Clement         OS << "}\n";
6153060894bSValentin Clement       } else {
6163060894bSValentin Clement         OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass()
6173060894bSValentin Clement            << "::" << Clause.getFormattedParserClassName() << " &x) {\n";
6183060894bSValentin Clement         OS << "  Word(\"" << Clause.getName().upper() << "\");\n";
6193060894bSValentin Clement         OS << "  Put(\"(\");\n";
6203060894bSValentin Clement         if (Clause.isValueList())
6213060894bSValentin Clement           OS << "  Walk(x.v, \",\");\n";
6223060894bSValentin Clement         else
6233060894bSValentin Clement           OS << "  Walk(x.v);\n";
6243060894bSValentin Clement         OS << "  Put(\")\");\n";
6253060894bSValentin Clement         OS << "}\n";
6263060894bSValentin Clement       }
6273060894bSValentin Clement     } else {
6283060894bSValentin Clement       OS << "void Before(const " << DirLang.getFlangClauseBaseClass()
6293060894bSValentin Clement          << "::" << Clause.getFormattedParserClassName() << " &) { Word(\""
6303060894bSValentin Clement          << Clause.getName().upper() << "\"); }\n";
6313060894bSValentin Clement     }
6323060894bSValentin Clement   }
6333060894bSValentin Clement }
6343060894bSValentin Clement 
6353f46c209SValentin Clement // Generate check in the Enter functions for clauses classes.
GenerateFlangClauseCheckPrototypes(const DirectiveLanguage & DirLang,raw_ostream & OS)6363f46c209SValentin Clement void GenerateFlangClauseCheckPrototypes(const DirectiveLanguage &DirLang,
6373f46c209SValentin Clement                                         raw_ostream &OS) {
6383f46c209SValentin Clement 
6393f46c209SValentin Clement   IfDefScope Scope("GEN_FLANG_CLAUSE_CHECK_ENTER", OS);
6403f46c209SValentin Clement 
6413f46c209SValentin Clement   OS << "\n";
6423f46c209SValentin Clement   for (const auto &C : DirLang.getClauses()) {
6433f46c209SValentin Clement     Clause Clause{C};
6443f46c209SValentin Clement     OS << "void Enter(const parser::" << DirLang.getFlangClauseBaseClass()
6453f46c209SValentin Clement        << "::" << Clause.getFormattedParserClassName() << " &);\n";
6463f46c209SValentin Clement   }
6473f46c209SValentin Clement }
6483f46c209SValentin Clement 
6491d7960a6SPraveen // Generate the mapping for clauses between the parser class and the
6501d7960a6SPraveen // corresponding clause Kind
GenerateFlangClauseParserKindMap(const DirectiveLanguage & DirLang,raw_ostream & OS)6511d7960a6SPraveen void GenerateFlangClauseParserKindMap(const DirectiveLanguage &DirLang,
6521d7960a6SPraveen                                       raw_ostream &OS) {
6531d7960a6SPraveen 
6541d7960a6SPraveen   IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_KIND_MAP", OS);
6551d7960a6SPraveen 
6561d7960a6SPraveen   OS << "\n";
6571d7960a6SPraveen   for (const auto &C : DirLang.getClauses()) {
6581d7960a6SPraveen     Clause Clause{C};
6591d7960a6SPraveen     OS << "if constexpr (std::is_same_v<A, parser::"
6601d7960a6SPraveen        << DirLang.getFlangClauseBaseClass()
6611d7960a6SPraveen        << "::" << Clause.getFormattedParserClassName();
6621d7960a6SPraveen     OS << ">)\n";
6631d7960a6SPraveen     OS << "  return llvm::" << DirLang.getCppNamespace()
6641d7960a6SPraveen        << "::Clause::" << DirLang.getClausePrefix() << Clause.getFormattedName()
6651d7960a6SPraveen        << ";\n";
6661d7960a6SPraveen   }
6671d7960a6SPraveen 
6681d7960a6SPraveen   OS << "llvm_unreachable(\"Invalid " << DirLang.getName()
6691d7960a6SPraveen      << " Parser clause\");\n";
6701d7960a6SPraveen }
6711d7960a6SPraveen 
compareClauseName(Record * R1,Record * R2)672048aaab1SValentin Clement bool compareClauseName(Record *R1, Record *R2) {
673048aaab1SValentin Clement   Clause C1{R1};
674048aaab1SValentin Clement   Clause C2{R2};
675048aaab1SValentin Clement   return (C1.getName() > C2.getName());
676048aaab1SValentin Clement }
677048aaab1SValentin Clement 
678048aaab1SValentin Clement // Generate the parser for the clauses.
GenerateFlangClausesParser(const DirectiveLanguage & DirLang,raw_ostream & OS)679048aaab1SValentin Clement void GenerateFlangClausesParser(const DirectiveLanguage &DirLang,
680048aaab1SValentin Clement                                 raw_ostream &OS) {
681048aaab1SValentin Clement   std::vector<Record *> Clauses = DirLang.getClauses();
682048aaab1SValentin Clement   // Sort clauses in reverse alphabetical order so with clauses with same
683048aaab1SValentin Clement   // beginning, the longer option is tried before.
684*aba43035SDmitri Gribenko   llvm::sort(Clauses, compareClauseName);
685048aaab1SValentin Clement   IfDefScope Scope("GEN_FLANG_CLAUSES_PARSER", OS);
686048aaab1SValentin Clement   OS << "\n";
687048aaab1SValentin Clement   unsigned index = 0;
688048aaab1SValentin Clement   unsigned lastClauseIndex = DirLang.getClauses().size() - 1;
689048aaab1SValentin Clement   OS << "TYPE_PARSER(\n";
690048aaab1SValentin Clement   for (const auto &C : Clauses) {
691048aaab1SValentin Clement     Clause Clause{C};
692048aaab1SValentin Clement     if (Clause.getAliases().empty()) {
693048aaab1SValentin Clement       OS << "  \"" << Clause.getName() << "\"";
694048aaab1SValentin Clement     } else {
695048aaab1SValentin Clement       OS << "  ("
696048aaab1SValentin Clement          << "\"" << Clause.getName() << "\"_tok";
697048aaab1SValentin Clement       for (StringRef alias : Clause.getAliases()) {
698048aaab1SValentin Clement         OS << " || \"" << alias << "\"_tok";
699048aaab1SValentin Clement       }
700048aaab1SValentin Clement       OS << ")";
701048aaab1SValentin Clement     }
702048aaab1SValentin Clement 
703048aaab1SValentin Clement     OS << " >> construct<" << DirLang.getFlangClauseBaseClass()
704048aaab1SValentin Clement        << ">(construct<" << DirLang.getFlangClauseBaseClass()
705048aaab1SValentin Clement        << "::" << Clause.getFormattedParserClassName() << ">(";
706048aaab1SValentin Clement     if (Clause.getFlangClass().empty()) {
707048aaab1SValentin Clement       OS << "))";
708048aaab1SValentin Clement       if (index != lastClauseIndex)
709048aaab1SValentin Clement         OS << " ||";
710048aaab1SValentin Clement       OS << "\n";
711048aaab1SValentin Clement       ++index;
712048aaab1SValentin Clement       continue;
713048aaab1SValentin Clement     }
714048aaab1SValentin Clement 
715048aaab1SValentin Clement     if (Clause.isValueOptional())
716048aaab1SValentin Clement       OS << "maybe(";
717048aaab1SValentin Clement     OS << "parenthesized(";
718048aaab1SValentin Clement 
719048aaab1SValentin Clement     if (!Clause.getPrefix().empty())
720048aaab1SValentin Clement       OS << "\"" << Clause.getPrefix() << ":\" >> ";
721048aaab1SValentin Clement 
722048aaab1SValentin Clement     // The common Flang parser are used directly. Their name is identical to
723048aaab1SValentin Clement     // the Flang class with first letter as lowercase. If the Flang class is
724048aaab1SValentin Clement     // not a common class, we assume there is a specific Parser<>{} with the
725048aaab1SValentin Clement     // Flang class name provided.
726048aaab1SValentin Clement     llvm::SmallString<128> Scratch;
727048aaab1SValentin Clement     StringRef Parser =
728048aaab1SValentin Clement         llvm::StringSwitch<StringRef>(Clause.getFlangClass())
729048aaab1SValentin Clement             .Case("Name", "name")
730048aaab1SValentin Clement             .Case("ScalarIntConstantExpr", "scalarIntConstantExpr")
731048aaab1SValentin Clement             .Case("ScalarIntExpr", "scalarIntExpr")
732048aaab1SValentin Clement             .Case("ScalarLogicalExpr", "scalarLogicalExpr")
733048aaab1SValentin Clement             .Default(("Parser<" + Clause.getFlangClass() + ">{}")
734048aaab1SValentin Clement                          .toStringRef(Scratch));
735048aaab1SValentin Clement     OS << Parser;
736048aaab1SValentin Clement     if (!Clause.getPrefix().empty() && Clause.isPrefixOptional())
737048aaab1SValentin Clement       OS << " || " << Parser;
738048aaab1SValentin Clement     OS << ")"; // close parenthesized(.
739048aaab1SValentin Clement 
740048aaab1SValentin Clement     if (Clause.isValueOptional()) // close maybe(.
741048aaab1SValentin Clement       OS << ")";
742048aaab1SValentin Clement     OS << "))";
743048aaab1SValentin Clement     if (index != lastClauseIndex)
744048aaab1SValentin Clement       OS << " ||";
745048aaab1SValentin Clement     OS << "\n";
746048aaab1SValentin Clement     ++index;
747048aaab1SValentin Clement   }
748048aaab1SValentin Clement   OS << ")\n";
749048aaab1SValentin Clement }
750048aaab1SValentin Clement 
75128b00ba7Sclementval // Generate the implementation section for the enumeration in the directive
7521a70077bSValentin Clement // language
EmitDirectivesFlangImpl(const DirectiveLanguage & DirLang,raw_ostream & OS)7536c337945SValentin Clement void EmitDirectivesFlangImpl(const DirectiveLanguage &DirLang,
7546c337945SValentin Clement                              raw_ostream &OS) {
7556e42a417SValentin Clement 
7566c337945SValentin Clement   GenerateDirectiveClauseSets(DirLang, OS);
7576e42a417SValentin Clement 
7586c337945SValentin Clement   GenerateDirectiveClauseMap(DirLang, OS);
75916c1d251SValentin Clement 
7606c337945SValentin Clement   GenerateFlangClauseParserClass(DirLang, OS);
76116c1d251SValentin Clement 
7626c337945SValentin Clement   GenerateFlangClauseParserClassList(DirLang, OS);
76316c1d251SValentin Clement 
7646c337945SValentin Clement   GenerateFlangClauseDump(DirLang, OS);
7653060894bSValentin Clement 
7666c337945SValentin Clement   GenerateFlangClauseUnparse(DirLang, OS);
7673f46c209SValentin Clement 
7683f46c209SValentin Clement   GenerateFlangClauseCheckPrototypes(DirLang, OS);
7691d7960a6SPraveen 
7701d7960a6SPraveen   GenerateFlangClauseParserKindMap(DirLang, OS);
771048aaab1SValentin Clement 
772048aaab1SValentin Clement   GenerateFlangClausesParser(DirLang, OS);
7736e42a417SValentin Clement }
7746e42a417SValentin Clement 
GenerateClauseClassMacro(const DirectiveLanguage & DirLang,raw_ostream & OS)775f4c8b803SValentin Clement void GenerateClauseClassMacro(const DirectiveLanguage &DirLang,
776f4c8b803SValentin Clement                               raw_ostream &OS) {
777f4c8b803SValentin Clement   // Generate macros style information for legacy code in clang
778f4c8b803SValentin Clement   IfDefScope Scope("GEN_CLANG_CLAUSE_CLASS", OS);
779f4c8b803SValentin Clement 
780f4c8b803SValentin Clement   OS << "\n";
781f4c8b803SValentin Clement 
782f4c8b803SValentin Clement   OS << "#ifndef CLAUSE\n";
783f4c8b803SValentin Clement   OS << "#define CLAUSE(Enum, Str, Implicit)\n";
784f4c8b803SValentin Clement   OS << "#endif\n";
785f4c8b803SValentin Clement   OS << "#ifndef CLAUSE_CLASS\n";
786f4c8b803SValentin Clement   OS << "#define CLAUSE_CLASS(Enum, Str, Class)\n";
787f4c8b803SValentin Clement   OS << "#endif\n";
788f4c8b803SValentin Clement   OS << "#ifndef CLAUSE_NO_CLASS\n";
789f4c8b803SValentin Clement   OS << "#define CLAUSE_NO_CLASS(Enum, Str)\n";
790f4c8b803SValentin Clement   OS << "#endif\n";
791f4c8b803SValentin Clement   OS << "\n";
792f4c8b803SValentin Clement   OS << "#define __CLAUSE(Name, Class)                      \\\n";
793f4c8b803SValentin Clement   OS << "  CLAUSE(" << DirLang.getClausePrefix()
794f4c8b803SValentin Clement      << "##Name, #Name, /* Implicit */ false) \\\n";
795f4c8b803SValentin Clement   OS << "  CLAUSE_CLASS(" << DirLang.getClausePrefix()
796f4c8b803SValentin Clement      << "##Name, #Name, Class)\n";
797f4c8b803SValentin Clement   OS << "#define __CLAUSE_NO_CLASS(Name)                    \\\n";
798f4c8b803SValentin Clement   OS << "  CLAUSE(" << DirLang.getClausePrefix()
799f4c8b803SValentin Clement      << "##Name, #Name, /* Implicit */ false) \\\n";
800f4c8b803SValentin Clement   OS << "  CLAUSE_NO_CLASS(" << DirLang.getClausePrefix() << "##Name, #Name)\n";
801f4c8b803SValentin Clement   OS << "#define __IMPLICIT_CLAUSE_CLASS(Name, Str, Class)  \\\n";
802f4c8b803SValentin Clement   OS << "  CLAUSE(" << DirLang.getClausePrefix()
803f4c8b803SValentin Clement      << "##Name, Str, /* Implicit */ true)    \\\n";
804f4c8b803SValentin Clement   OS << "  CLAUSE_CLASS(" << DirLang.getClausePrefix()
805f4c8b803SValentin Clement      << "##Name, Str, Class)\n";
806f4c8b803SValentin Clement   OS << "#define __IMPLICIT_CLAUSE_NO_CLASS(Name, Str)      \\\n";
807f4c8b803SValentin Clement   OS << "  CLAUSE(" << DirLang.getClausePrefix()
808f4c8b803SValentin Clement      << "##Name, Str, /* Implicit */ true)    \\\n";
809f4c8b803SValentin Clement   OS << "  CLAUSE_NO_CLASS(" << DirLang.getClausePrefix() << "##Name, Str)\n";
810f4c8b803SValentin Clement   OS << "\n";
811f4c8b803SValentin Clement 
812f4c8b803SValentin Clement   for (const auto &R : DirLang.getClauses()) {
813f4c8b803SValentin Clement     Clause C{R};
814f4c8b803SValentin Clement     if (C.getClangClass().empty()) { // NO_CLASS
815f4c8b803SValentin Clement       if (C.isImplicit()) {
816f4c8b803SValentin Clement         OS << "__IMPLICIT_CLAUSE_NO_CLASS(" << C.getFormattedName() << ", \""
817f4c8b803SValentin Clement            << C.getFormattedName() << "\")\n";
818f4c8b803SValentin Clement       } else {
819f4c8b803SValentin Clement         OS << "__CLAUSE_NO_CLASS(" << C.getFormattedName() << ")\n";
820f4c8b803SValentin Clement       }
821f4c8b803SValentin Clement     } else { // CLASS
822f4c8b803SValentin Clement       if (C.isImplicit()) {
823f4c8b803SValentin Clement         OS << "__IMPLICIT_CLAUSE_CLASS(" << C.getFormattedName() << ", \""
824f4c8b803SValentin Clement            << C.getFormattedName() << "\", " << C.getClangClass() << ")\n";
825f4c8b803SValentin Clement       } else {
826f4c8b803SValentin Clement         OS << "__CLAUSE(" << C.getFormattedName() << ", " << C.getClangClass()
827f4c8b803SValentin Clement            << ")\n";
828f4c8b803SValentin Clement       }
829f4c8b803SValentin Clement     }
830f4c8b803SValentin Clement   }
831f4c8b803SValentin Clement 
832f4c8b803SValentin Clement   OS << "\n";
833f4c8b803SValentin Clement   OS << "#undef __IMPLICIT_CLAUSE_NO_CLASS\n";
834f4c8b803SValentin Clement   OS << "#undef __IMPLICIT_CLAUSE_CLASS\n";
835f4c8b803SValentin Clement   OS << "#undef __CLAUSE\n";
836f4c8b803SValentin Clement   OS << "#undef CLAUSE_NO_CLASS\n";
837f4c8b803SValentin Clement   OS << "#undef CLAUSE_CLASS\n";
838f4c8b803SValentin Clement   OS << "#undef CLAUSE\n";
839f4c8b803SValentin Clement }
840f4c8b803SValentin Clement 
841d709dcc0SValentin Clement // Generate the implemenation for the enumeration in the directive
8426e42a417SValentin Clement // language. This code can be included in library.
EmitDirectivesBasicImpl(const DirectiveLanguage & DirLang,raw_ostream & OS)843d709dcc0SValentin Clement void EmitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
844d709dcc0SValentin Clement                              raw_ostream &OS) {
845d709dcc0SValentin Clement   IfDefScope Scope("GEN_DIRECTIVES_IMPL", OS);
846943660fdSValentin Clement 
8471a70077bSValentin Clement   // getDirectiveKind(StringRef Str)
8486c337945SValentin Clement   GenerateGetKind(DirLang.getDirectives(), OS, "Directive", DirLang,
849e8d4038eSValentin Clement                   DirLang.getDirectivePrefix(), /*ImplicitAsUnknown=*/false);
8501a70077bSValentin Clement 
8511a70077bSValentin Clement   // getDirectiveName(Directive Kind)
8526c337945SValentin Clement   GenerateGetName(DirLang.getDirectives(), OS, "Directive", DirLang,
853e8d4038eSValentin Clement                   DirLang.getDirectivePrefix());
8541a70077bSValentin Clement 
8551a70077bSValentin Clement   // getClauseKind(StringRef Str)
8566c337945SValentin Clement   GenerateGetKind(DirLang.getClauses(), OS, "Clause", DirLang,
8576c337945SValentin Clement                   DirLang.getClausePrefix(),
858e8d4038eSValentin Clement                   /*ImplicitAsUnknown=*/true);
8591a70077bSValentin Clement 
8601a70077bSValentin Clement   // getClauseName(Clause Kind)
8616c337945SValentin Clement   GenerateGetName(DirLang.getClauses(), OS, "Clause", DirLang,
8626c337945SValentin Clement                   DirLang.getClausePrefix());
86365482e8aSValentin Clement 
864e6c5e6efSKiran Chandramohan   // get<ClauseVal>Kind(StringRef Str)
8656c337945SValentin Clement   GenerateGetKindClauseVal(DirLang, OS);
866e6c5e6efSKiran Chandramohan 
8676e42a417SValentin Clement   // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version)
8686c337945SValentin Clement   GenerateIsAllowedClause(DirLang, OS);
8691a70077bSValentin Clement }
8701a70077bSValentin Clement 
871d709dcc0SValentin Clement // Generate the implemenation section for the enumeration in the directive
872d709dcc0SValentin Clement // language.
EmitDirectivesImpl(RecordKeeper & Records,raw_ostream & OS)873d709dcc0SValentin Clement void EmitDirectivesImpl(RecordKeeper &Records, raw_ostream &OS) {
874d709dcc0SValentin Clement   const auto DirLang = DirectiveLanguage{Records};
875d709dcc0SValentin Clement   if (DirLang.HasValidityErrors())
876d709dcc0SValentin Clement     return;
877d709dcc0SValentin Clement 
878d709dcc0SValentin Clement   EmitDirectivesFlangImpl(DirLang, OS);
879d709dcc0SValentin Clement 
880d709dcc0SValentin Clement   GenerateClauseClassMacro(DirLang, OS);
881d709dcc0SValentin Clement 
882d709dcc0SValentin Clement   EmitDirectivesBasicImpl(DirLang, OS);
883d709dcc0SValentin Clement }
884d709dcc0SValentin Clement 
885d90443b1SValentin Clement } // namespace llvm
886