1 | //===========- DirectiveCommonGen.cpp - Directive common info generator -=====// |
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 | // OpenMPCommonGen generates utility information from the single OpenMP source |
10 | // of truth in llvm/lib/Frontend/OpenMP. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "mlir/TableGen/GenInfo.h" |
15 | |
16 | #include "llvm/ADT/Twine.h" |
17 | #include "llvm/Support/CommandLine.h" |
18 | #include "llvm/Support/raw_ostream.h" |
19 | #include "llvm/TableGen/DirectiveEmitter.h" |
20 | #include "llvm/TableGen/Error.h" |
21 | #include "llvm/TableGen/Record.h" |
22 | |
23 | using llvm::Clause; |
24 | using llvm::ClauseVal; |
25 | using llvm::raw_ostream; |
26 | using llvm::RecordKeeper; |
27 | |
28 | // LLVM has multiple places (Clang, Flang, MLIR) where information about |
29 | // the directives (OpenMP/OpenACC), and clauses are needed. It is good software |
30 | // engineering to keep the common information in a single place to avoid |
31 | // duplication, reduce engineering effort and prevent mistakes. |
32 | // Currently that common place is llvm/include/llvm/Frontend/OpenMP/OMP.td for |
33 | // OpenMP and llvm/include/llvm/Frontend/OpenACC/ACC.td for OpenACC. |
34 | // We plan to use this tablegen source to generate all the required |
35 | // declarations, functions etc. |
36 | // |
37 | // Some OpenMP/OpenACC clauses accept only a fixed set of values as inputs. |
38 | // These can be represented as a Enum Attributes (EnumAttrDef) in MLIR |
39 | // ODS. The emitDecls function below currently generates these enumerations. The |
40 | // name of the enumeration is specified in the enumClauseValue field of |
41 | // Clause record in OMP.td. This name can be used to specify the type of the |
42 | // OpenMP operation's operand. The allowedClauseValues field provides the list |
43 | // of ClauseValues which are part of the enumeration. |
44 | static bool emitDecls(const RecordKeeper &recordKeeper, llvm::StringRef dialect, |
45 | raw_ostream &os) { |
46 | // A dialect must be selected for the generated attributes. |
47 | if (dialect.empty()) { |
48 | llvm::PrintFatalError(Msg: "a dialect must be selected for the directives via " |
49 | "'--directives-dialect'" ); |
50 | } |
51 | |
52 | const auto &directiveLanguages = |
53 | recordKeeper.getAllDerivedDefinitions(ClassName: "DirectiveLanguage" ); |
54 | assert(!directiveLanguages.empty() && "DirectiveLanguage missing." ); |
55 | |
56 | const auto &clauses = recordKeeper.getAllDerivedDefinitions(ClassName: "Clause" ); |
57 | |
58 | for (const auto &r : clauses) { |
59 | Clause c{r}; |
60 | const auto &clauseVals = c.getClauseVals(); |
61 | if (clauseVals.empty()) |
62 | continue; |
63 | |
64 | const auto enumName = c.getEnumName(); |
65 | assert(!enumName.empty() && "enumClauseValue field not set." ); |
66 | |
67 | std::vector<std::string> cvDefs; |
68 | for (const auto &it : llvm::enumerate(First: clauseVals)) { |
69 | ClauseVal cval{it.value()}; |
70 | if (!cval.isUserVisible()) |
71 | continue; |
72 | |
73 | std::string name = cval.getFormattedName(); |
74 | std::string enumValName(name.length(), ' '); |
75 | std::transform(first: name.begin(), last: name.end(), result: enumValName.begin(), |
76 | unary_op: llvm::toLower); |
77 | enumValName[0] = llvm::toUpper(x: enumValName[0]); |
78 | std::string cvDef{(enumName + llvm::Twine(name)).str()}; |
79 | os << "def " << cvDef << " : I32EnumAttrCase<\"" << enumValName << "\", " |
80 | << it.index() << ", \"" << name << "\">;\n" ; |
81 | cvDefs.push_back(x: cvDef); |
82 | } |
83 | |
84 | os << "def " << enumName << ": I32EnumAttr<\n" ; |
85 | os << " \"Clause" << enumName << "\",\n" ; |
86 | os << " \"" << enumName << " Clause\",\n" ; |
87 | os << " [" ; |
88 | for (unsigned int i = 0; i < cvDefs.size(); i++) { |
89 | os << cvDefs[i]; |
90 | if (i != cvDefs.size() - 1) |
91 | os << "," ; |
92 | } |
93 | os << "]> {\n" ; |
94 | os << " let cppNamespace = \"::mlir::" |
95 | << directiveLanguages[0]->getValueAsString(FieldName: "cppNamespace" ) << "\";\n" ; |
96 | os << " let genSpecializedAttr = 0;\n" ; |
97 | os << "}\n" ; |
98 | llvm::SmallString<16> mnemonic; |
99 | llvm::transform(Range: enumName, d_first: std::back_inserter(x&: mnemonic), F: llvm::toLower); |
100 | os << "def " << enumName << "Attr : EnumAttr<" << dialect << "_Dialect, " |
101 | << enumName << ", \"" << mnemonic << "\">;\n" ; |
102 | } |
103 | return false; |
104 | } |
105 | |
106 | static llvm::cl::OptionCategory |
107 | directiveGenCat("Options for gen-directive-decl" ); |
108 | static llvm::cl::opt<std::string> |
109 | dialect("directives-dialect" , |
110 | llvm::cl::desc("Generate directives for this dialect" ), |
111 | llvm::cl::cat(directiveGenCat), llvm::cl::CommaSeparated); |
112 | |
113 | // Registers the generator to mlir-tblgen. |
114 | static mlir::GenRegistration genDirectiveDecls( |
115 | "gen-directive-decl" , |
116 | "Generate declarations for directives (OpenMP/OpenACC etc.)" , |
117 | [](const RecordKeeper &records, raw_ostream &os) { |
118 | return emitDecls(recordKeeper: records, dialect, os); |
119 | }); |
120 | |