1 //===- OmpOpGen.cpp - OpenMP dialect op specific generators ---------------===//
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
7 //===----------------------------------------------------------------------===//
9 // OmpOpGen defines OpenMP dialect operation specific generators.
11 //===----------------------------------------------------------------------===//
13 #include "mlir/TableGen/GenInfo.h"
15 #include "llvm/TableGen/Error.h"
16 #include "llvm/TableGen/Record.h"
20 /// Obtain the name of the OpenMP clause a given record inheriting
21 /// `OpenMP_Clause` refers to.
23 /// It supports direct and indirect `OpenMP_Clause` superclasses. Once the
24 /// `OpenMP_Clause` class the record is based on is found, the optional
25 /// "OpenMP_" prefix and "Skip" and "Clause" suffixes are removed to return only
26 /// the clause name, i.e. "OpenMP_CollapseClauseSkip" is returned as "Collapse".
27 static StringRef
extractOmpClauseName(Record
*clause
) {
28 Record
*ompClause
= clause
->getRecords().getClass("OpenMP_Clause");
29 assert(ompClause
&& "base OpenMP records expected to be defined");
31 StringRef clauseClassName
;
32 SmallVector
<Record
*, 1> clauseSuperClasses
;
33 clause
->getDirectSuperClasses(clauseSuperClasses
);
35 // Check if OpenMP_Clause is a direct superclass.
36 for (Record
*superClass
: clauseSuperClasses
) {
37 if (superClass
== ompClause
) {
38 clauseClassName
= clause
->getName();
43 // Support indirectly-inherited OpenMP_Clauses.
44 if (clauseClassName
.empty()) {
45 for (auto [superClass
, _
] : clause
->getSuperClasses()) {
46 if (superClass
->isSubClassOf(ompClause
)) {
47 clauseClassName
= superClass
->getName();
53 assert(!clauseClassName
.empty() && "clause name must be found");
55 // Keep only the OpenMP clause name itself for reporting purposes.
56 StringRef prefix
= "OpenMP_";
57 StringRef suffixes
[] = {"Skip", "Clause"};
59 if (clauseClassName
.starts_with(prefix
))
60 clauseClassName
= clauseClassName
.substr(prefix
.size());
62 for (StringRef suffix
: suffixes
) {
63 if (clauseClassName
.ends_with(suffix
))
65 clauseClassName
.substr(0, clauseClassName
.size() - suffix
.size());
68 return clauseClassName
;
71 /// Check that the given argument, identified by its name and initialization
72 /// value, is present in the \c arguments `dag`.
73 static bool verifyArgument(DagInit
*arguments
, StringRef argName
,
75 auto range
= zip_equal(arguments
->getArgNames(), arguments
->getArgs());
77 range
.begin(), range
.end(),
78 [&](std::tuple
<llvm::StringInit
*const &, llvm::Init
*const &> v
) {
79 return std::get
<0>(v
)->getAsUnquotedString() == argName
&&
80 std::get
<1>(v
) == argInit
;
84 /// Check that the given string record value, identified by its name \c value,
85 /// is either undefined or empty in both the given operation and clause record
86 /// or its contents for the clause record are contained in the operation record.
87 static bool verifyStringValue(StringRef value
, Record
*op
, Record
*clause
) {
88 auto opValue
= op
->getValueAsOptionalString(value
);
89 auto clauseValue
= clause
->getValueAsOptionalString(value
);
91 bool opHasValue
= opValue
&& !opValue
->trim().empty();
92 bool clauseHasValue
= clauseValue
&& !clauseValue
->trim().empty();
95 return !clauseHasValue
;
97 return !clauseHasValue
|| opValue
->contains(clauseValue
->trim());
100 /// Verify that all fields of the given clause not explicitly ignored are
101 /// present in the corresponding operation field.
103 /// Print warnings or errors where this is not the case.
104 static void verifyClause(Record
*op
, Record
*clause
) {
105 StringRef clauseClassName
= extractOmpClauseName(clause
);
107 if (!clause
->getValueAsBit("ignoreArgs")) {
108 DagInit
*opArguments
= op
->getValueAsDag("arguments");
109 DagInit
*arguments
= clause
->getValueAsDag("arguments");
111 for (auto [name
, arg
] :
112 zip(arguments
->getArgNames(), arguments
->getArgs())) {
113 if (!verifyArgument(opArguments
, name
->getAsUnquotedString(), arg
))
116 "'" + clauseClassName
+ "' clause-defined argument '" +
117 arg
->getAsUnquotedString() + ":$" +
118 name
->getAsUnquotedString() +
119 "' not present in operation. Consider `dag arguments = "
120 "!con(clausesArgs, ...)` or explicitly skipping this field.");
124 if (!clause
->getValueAsBit("ignoreAsmFormat") &&
125 !verifyStringValue("assemblyFormat", op
, clause
))
128 "'" + clauseClassName
+
129 "' clause-defined `assemblyFormat` not present in operation. "
130 "Consider concatenating `clausesAssemblyFormat` or explicitly "
131 "skipping this field.");
133 if (!clause
->getValueAsBit("ignoreDesc") &&
134 !verifyStringValue("description", op
, clause
))
135 PrintError(op
->getLoc(),
136 "'" + clauseClassName
+
137 "' clause-defined `description` not present in operation. "
138 "Consider concatenating `clausesDescription` or explicitly "
139 "skipping this field.");
141 if (!clause
->getValueAsBit("ignoreExtraDecl") &&
142 !verifyStringValue("extraClassDeclaration", op
, clause
))
145 "'" + clauseClassName
+
146 "' clause-defined `extraClassDeclaration` not present in "
147 "operation. Consider concatenating `clausesExtraClassDeclaration` "
148 "or explicitly skipping this field.");
151 /// Verify that all properties of `OpenMP_Clause`s of records deriving from
152 /// `OpenMP_Op`s have been inherited by the latter.
153 static bool verifyDecls(const RecordKeeper
&recordKeeper
, raw_ostream
&) {
154 for (Record
*op
: recordKeeper
.getAllDerivedDefinitions("OpenMP_Op")) {
155 for (Record
*clause
: op
->getValueAsListOfDefs("clauseList"))
156 verifyClause(op
, clause
);
162 // Registers the generator to mlir-tblgen.
163 static mlir::GenRegistration
164 verifyOpenmpOps("verify-openmp-ops",
165 "Verify OpenMP operations (produce no output file)",