blob: 51eb43f322e6ad9ab7c0e312c6e1d5055589f166 [file]
//===- OmpOpGen.cpp - OpenMP dialect op specific generators ---------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// OmpOpGen defines OpenMP dialect operation specific generators.
//
//===----------------------------------------------------------------------===//
#include "mlir/TableGen/GenInfo.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
using namespace llvm;
/// Obtain the name of the OpenMP clause a given record inheriting
/// `OpenMP_Clause` refers to.
///
/// It supports direct and indirect `OpenMP_Clause` superclasses. Once the
/// `OpenMP_Clause` class the record is based on is found, the optional
/// "OpenMP_" prefix and "Skip" and "Clause" suffixes are removed to return only
/// the clause name, i.e. "OpenMP_CollapseClauseSkip" is returned as "Collapse".
static StringRef extractOmpClauseName(Record *clause) {
Record *ompClause = clause->getRecords().getClass("OpenMP_Clause");
assert(ompClause && "base OpenMP records expected to be defined");
StringRef clauseClassName;
SmallVector<Record *, 1> clauseSuperClasses;
clause->getDirectSuperClasses(clauseSuperClasses);
// Check if OpenMP_Clause is a direct superclass.
for (Record *superClass : clauseSuperClasses) {
if (superClass == ompClause) {
clauseClassName = clause->getName();
break;
}
}
// Support indirectly-inherited OpenMP_Clauses.
if (clauseClassName.empty()) {
for (auto [superClass, _] : clause->getSuperClasses()) {
if (superClass->isSubClassOf(ompClause)) {
clauseClassName = superClass->getName();
break;
}
}
}
assert(!clauseClassName.empty() && "clause name must be found");
// Keep only the OpenMP clause name itself for reporting purposes.
StringRef prefix = "OpenMP_";
StringRef suffixes[] = {"Skip", "Clause"};
if (clauseClassName.starts_with(prefix))
clauseClassName = clauseClassName.substr(prefix.size());
for (StringRef suffix : suffixes) {
if (clauseClassName.ends_with(suffix))
clauseClassName =
clauseClassName.substr(0, clauseClassName.size() - suffix.size());
}
return clauseClassName;
}
/// Check that the given argument, identified by its name and initialization
/// value, is present in the \c arguments `dag`.
static bool verifyArgument(DagInit *arguments, StringRef argName,
Init *argInit) {
auto range = zip_equal(arguments->getArgNames(), arguments->getArgs());
return std::find_if(
range.begin(), range.end(),
[&](std::tuple<llvm::StringInit *const &, llvm::Init *const &> v) {
return std::get<0>(v)->getAsUnquotedString() == argName &&
std::get<1>(v) == argInit;
}) != range.end();
}
/// Check that the given string record value, identified by its name \c value,
/// is either undefined or empty in both the given operation and clause record
/// or its contents for the clause record are contained in the operation record.
static bool verifyStringValue(StringRef value, Record *op, Record *clause) {
auto opValue = op->getValueAsOptionalString(value);
auto clauseValue = clause->getValueAsOptionalString(value);
bool opHasValue = opValue && !opValue->trim().empty();
bool clauseHasValue = clauseValue && !clauseValue->trim().empty();
if (!opHasValue)
return !clauseHasValue;
return !clauseHasValue || opValue->contains(clauseValue->trim());
}
/// Verify that all fields of the given clause not explicitly ignored are
/// present in the corresponding operation field.
///
/// Print warnings or errors where this is not the case.
static void verifyClause(Record *op, Record *clause) {
StringRef clauseClassName = extractOmpClauseName(clause);
if (!clause->getValueAsBit("ignoreArgs")) {
DagInit *opArguments = op->getValueAsDag("arguments");
DagInit *arguments = clause->getValueAsDag("arguments");
for (auto [name, arg] :
zip(arguments->getArgNames(), arguments->getArgs())) {
if (!verifyArgument(opArguments, name->getAsUnquotedString(), arg))
PrintWarning(
op->getLoc(),
"'" + clauseClassName + "' clause-defined argument '" +
arg->getAsUnquotedString() + ":$" +
name->getAsUnquotedString() +
"' not present in operation. Consider `dag arguments = "
"!con(clausesArgs, ...)` or explicitly skipping this field.");
}
}
if (!clause->getValueAsBit("ignoreAsmFormat") &&
!verifyStringValue("assemblyFormat", op, clause))
PrintWarning(
op->getLoc(),
"'" + clauseClassName +
"' clause-defined `assemblyFormat` not present in operation. "
"Consider concatenating `clausesAssemblyFormat` or explicitly "
"skipping this field.");
if (!clause->getValueAsBit("ignoreDesc") &&
!verifyStringValue("description", op, clause))
PrintError(op->getLoc(),
"'" + clauseClassName +
"' clause-defined `description` not present in operation. "
"Consider concatenating `clausesDescription` or explicitly "
"skipping this field.");
if (!clause->getValueAsBit("ignoreExtraDecl") &&
!verifyStringValue("extraClassDeclaration", op, clause))
PrintWarning(
op->getLoc(),
"'" + clauseClassName +
"' clause-defined `extraClassDeclaration` not present in "
"operation. Consider concatenating `clausesExtraClassDeclaration` "
"or explicitly skipping this field.");
}
/// Verify that all properties of `OpenMP_Clause`s of records deriving from
/// `OpenMP_Op`s have been inherited by the latter.
static bool verifyDecls(const RecordKeeper &recordKeeper, raw_ostream &) {
for (Record *op : recordKeeper.getAllDerivedDefinitions("OpenMP_Op")) {
for (Record *clause : op->getValueAsListOfDefs("clauseList"))
verifyClause(op, clause);
}
return false;
}
// Registers the generator to mlir-tblgen.
static mlir::GenRegistration
verifyOpenmpOps("verify-openmp-ops",
"Verify OpenMP operations (produce no output file)",
verifyDecls);