|
| 1 | +//===- OmpOpGen.cpp - OpenMP dialect op specific generators ---------------===// |
| 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 | +// OmpOpGen defines OpenMP dialect operation specific generators. |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +#include "mlir/TableGen/GenInfo.h" |
| 14 | + |
| 15 | +#include "llvm/TableGen/Error.h" |
| 16 | +#include "llvm/TableGen/Record.h" |
| 17 | + |
| 18 | +using namespace llvm; |
| 19 | + |
| 20 | +/// Obtain the name of the OpenMP clause a given record inheriting |
| 21 | +/// `OpenMP_Clause` refers to. |
| 22 | +/// |
| 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"); |
| 30 | + |
| 31 | + StringRef clauseClassName; |
| 32 | + SmallVector<Record *, 1> clauseSuperClasses; |
| 33 | + clause->getDirectSuperClasses(clauseSuperClasses); |
| 34 | + |
| 35 | + // Check if OpenMP_Clause is a direct superclass. |
| 36 | + for (Record *superClass : clauseSuperClasses) { |
| 37 | + if (superClass == ompClause) { |
| 38 | + clauseClassName = clause->getName(); |
| 39 | + break; |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 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(); |
| 48 | + break; |
| 49 | + } |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + assert(!clauseClassName.empty() && "clause name must be found"); |
| 54 | + |
| 55 | + // Keep only the OpenMP clause name itself for reporting purposes. |
| 56 | + StringRef prefix = "OpenMP_"; |
| 57 | + StringRef suffixes[] = {"Skip", "Clause"}; |
| 58 | + |
| 59 | + if (clauseClassName.starts_with(prefix)) |
| 60 | + clauseClassName = clauseClassName.substr(prefix.size()); |
| 61 | + |
| 62 | + for (StringRef suffix : suffixes) { |
| 63 | + if (clauseClassName.ends_with(suffix)) |
| 64 | + clauseClassName = |
| 65 | + clauseClassName.substr(0, clauseClassName.size() - suffix.size()); |
| 66 | + } |
| 67 | + |
| 68 | + return clauseClassName; |
| 69 | +} |
| 70 | + |
| 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, |
| 74 | + Init *argInit) { |
| 75 | + auto range = zip_equal(arguments->getArgNames(), arguments->getArgs()); |
| 76 | + return std::find_if( |
| 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; |
| 81 | + }) != range.end(); |
| 82 | +} |
| 83 | + |
| 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); |
| 90 | + |
| 91 | + bool opHasValue = opValue && !opValue->trim().empty(); |
| 92 | + bool clauseHasValue = clauseValue && !clauseValue->trim().empty(); |
| 93 | + |
| 94 | + if (!opHasValue) |
| 95 | + return !clauseHasValue; |
| 96 | + |
| 97 | + return !clauseHasValue || opValue->contains(clauseValue->trim()); |
| 98 | +} |
| 99 | + |
| 100 | +/// Verify that all fields of the given clause not explicitly ignored are |
| 101 | +/// present in the corresponding operation field. |
| 102 | +/// |
| 103 | +/// Print warnings or errors where this is not the case. |
| 104 | +static void verifyClause(Record *op, Record *clause) { |
| 105 | + StringRef clauseClassName = extractOmpClauseName(clause); |
| 106 | + |
| 107 | + if (!clause->getValueAsBit("ignoreArgs")) { |
| 108 | + DagInit *opArguments = op->getValueAsDag("arguments"); |
| 109 | + DagInit *arguments = clause->getValueAsDag("arguments"); |
| 110 | + |
| 111 | + for (auto [name, arg] : |
| 112 | + zip(arguments->getArgNames(), arguments->getArgs())) { |
| 113 | + if (!verifyArgument(opArguments, name->getAsUnquotedString(), arg)) |
| 114 | + PrintWarning( |
| 115 | + op->getLoc(), |
| 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."); |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + if (!clause->getValueAsBit("ignoreAsmFormat") && |
| 125 | + !verifyStringValue("assemblyFormat", op, clause)) |
| 126 | + PrintWarning( |
| 127 | + op->getLoc(), |
| 128 | + "'" + clauseClassName + |
| 129 | + "' clause-defined `assemblyFormat` not present in operation. " |
| 130 | + "Consider concatenating `clausesAssemblyFormat` or explicitly " |
| 131 | + "skipping this field."); |
| 132 | + |
| 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."); |
| 140 | + |
| 141 | + if (!clause->getValueAsBit("ignoreExtraDecl") && |
| 142 | + !verifyStringValue("extraClassDeclaration", op, clause)) |
| 143 | + PrintWarning( |
| 144 | + op->getLoc(), |
| 145 | + "'" + clauseClassName + |
| 146 | + "' clause-defined `extraClassDeclaration` not present in " |
| 147 | + "operation. Consider concatenating `clausesExtraClassDeclaration` " |
| 148 | + "or explicitly skipping this field."); |
| 149 | +} |
| 150 | + |
| 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); |
| 157 | + } |
| 158 | + |
| 159 | + return false; |
| 160 | +} |
| 161 | + |
| 162 | +// Registers the generator to mlir-tblgen. |
| 163 | +static mlir::GenRegistration |
| 164 | + verifyOpenmpOps("verify-openmp-ops", |
| 165 | + "Verify OpenMP operations (produce no output file)", |
| 166 | + verifyDecls); |
0 commit comments