| 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::EnumVal; |
| 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 &records, 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 | records.getAllDerivedDefinitions(ClassName: "DirectiveLanguage" ); |
| 54 | assert(!directiveLanguages.empty() && "DirectiveLanguage missing." ); |
| 55 | |
| 56 | for (const Clause c : records.getAllDerivedDefinitions(ClassName: "Clause" )) { |
| 57 | const auto &clauseVals = c.getClauseVals(); |
| 58 | if (clauseVals.empty()) |
| 59 | continue; |
| 60 | |
| 61 | const auto enumName = c.getEnumName(); |
| 62 | assert(!enumName.empty() && "enumClauseValue field not set." ); |
| 63 | |
| 64 | std::vector<std::string> cvDefs; |
| 65 | for (const auto &it : llvm::enumerate(First: clauseVals)) { |
| 66 | const EnumVal val{it.value()}; |
| 67 | if (!val.isUserVisible()) |
| 68 | continue; |
| 69 | |
| 70 | std::string name = val.getFormattedName(); |
| 71 | std::string enumValName(name.length(), ' '); |
| 72 | llvm::transform(Range&: name, d_first: enumValName.begin(), F: llvm::toLower); |
| 73 | enumValName[0] = llvm::toUpper(x: enumValName[0]); |
| 74 | std::string cvDef{(enumName + llvm::Twine(name)).str()}; |
| 75 | os << "def " << cvDef << " : I32EnumAttrCase<\"" << enumValName << "\", " |
| 76 | << it.index() << ", \"" << name << "\">;\n" ; |
| 77 | cvDefs.push_back(x: cvDef); |
| 78 | } |
| 79 | |
| 80 | os << "def " << enumName << ": I32EnumAttr<\n" ; |
| 81 | os << " \"Clause" << enumName << "\",\n" ; |
| 82 | os << " \"" << enumName << " Clause\",\n" ; |
| 83 | os << " [" ; |
| 84 | llvm::interleaveComma(c: cvDefs, os); |
| 85 | os << "]> {\n" ; |
| 86 | os << " let cppNamespace = \"::mlir::" |
| 87 | << directiveLanguages[0]->getValueAsString(FieldName: "cppNamespace" ) << "\";\n" ; |
| 88 | os << " let genSpecializedAttr = 0;\n" ; |
| 89 | os << "}\n" ; |
| 90 | llvm::SmallString<16> mnemonic; |
| 91 | llvm::transform(Range: enumName, d_first: std::back_inserter(x&: mnemonic), F: llvm::toLower); |
| 92 | os << "def " << enumName << "Attr : EnumAttr<" << dialect << "_Dialect, " |
| 93 | << enumName << ", \"" << mnemonic << "\">;\n" ; |
| 94 | } |
| 95 | return false; |
| 96 | } |
| 97 | |
| 98 | static llvm::cl::OptionCategory |
| 99 | directiveGenCat("Options for gen-directive-decl" ); |
| 100 | static llvm::cl::opt<std::string> |
| 101 | dialect("directives-dialect" , |
| 102 | llvm::cl::desc("Generate directives for this dialect" ), |
| 103 | llvm::cl::cat(directiveGenCat), llvm::cl::CommaSeparated); |
| 104 | |
| 105 | // Registers the generator to mlir-tblgen. |
| 106 | static mlir::GenRegistration genDirectiveDecls( |
| 107 | "gen-directive-decl" , |
| 108 | "Generate declarations for directives (OpenMP/OpenACC etc.)" , |
| 109 | [](const RecordKeeper &records, raw_ostream &os) { |
| 110 | return emitDecls(records, dialect, os); |
| 111 | }); |
| 112 | |