brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.2 KiB · e1be11a Raw
368 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// OmpOpGen defines OpenMP dialect operation specific generators.10//11//===----------------------------------------------------------------------===//12 13#include "mlir/TableGen/GenInfo.h"14 15#include "mlir/TableGen/CodeGenHelpers.h"16#include "llvm/ADT/StringExtras.h"17#include "llvm/ADT/StringSet.h"18#include "llvm/ADT/TypeSwitch.h"19#include "llvm/Support/FormatAdapters.h"20#include "llvm/TableGen/Error.h"21#include "llvm/TableGen/Record.h"22 23using namespace llvm;24 25/// The code block defining the base mixin class for combining clause operand26/// structures.27static const char *const baseMixinClass = R"(28namespace detail {29template <typename... Mixins>30struct Clauses : public Mixins... {};31} // namespace detail32)";33 34/// The code block defining operation argument structures.35static const char *const operationArgStruct = R"(36using {0}Operands = detail::Clauses<{1}>;37)";38 39/// Remove multiple optional prefixes and suffixes from \c str.40///41/// Prefixes and suffixes are attempted to be removed once in the order they42/// appear in the \c prefixes and \c suffixes arguments. All prefixes are43/// processed before suffixes are. This means it will behave as shown in the44/// following example:45///   - str: "PrePreNameSuf1Suf2"46///   - prefixes: ["Pre"]47///   - suffixes: ["Suf1", "Suf2"]48///   - return: "PreNameSuf1"49static StringRef stripPrefixAndSuffix(StringRef str,50                                      llvm::ArrayRef<StringRef> prefixes,51                                      llvm::ArrayRef<StringRef> suffixes) {52  for (StringRef prefix : prefixes)53    if (str.starts_with(prefix))54      str = str.drop_front(prefix.size());55 56  for (StringRef suffix : suffixes)57    if (str.ends_with(suffix))58      str = str.drop_back(suffix.size());59 60  return str;61}62 63/// Obtain the name of the OpenMP clause a given record inheriting64/// `OpenMP_Clause` refers to.65///66/// It supports direct and indirect `OpenMP_Clause` superclasses. Once the67/// `OpenMP_Clause` class the record is based on is found, the optional68/// "OpenMP_" prefix and "Skip" and "Clause" suffixes are removed to return only69/// the clause name, i.e. "OpenMP_CollapseClauseSkip" is returned as "Collapse".70static StringRef extractOmpClauseName(const Record *clause) {71  const Record *ompClause = clause->getRecords().getClass("OpenMP_Clause");72  assert(ompClause && "base OpenMP records expected to be defined");73 74  StringRef clauseClassName;75 76  // Check if OpenMP_Clause is a direct superclass.77  for (const Record *superClass :78       llvm::make_first_range(clause->getDirectSuperClasses())) {79    if (superClass == ompClause) {80      clauseClassName = clause->getName();81      break;82    }83  }84 85  // Support indirectly-inherited OpenMP_Clauses.86  if (clauseClassName.empty()) {87    for (const Record *superClass : clause->getSuperClasses()) {88      if (superClass->isSubClassOf(ompClause)) {89        clauseClassName = superClass->getName();90        break;91      }92    }93  }94 95  assert(!clauseClassName.empty() && "clause name must be found");96 97  // Keep only the OpenMP clause name itself for reporting purposes.98  return stripPrefixAndSuffix(clauseClassName, /*prefixes=*/{"OpenMP_"},99                              /*suffixes=*/{"Skip", "Clause"});100}101 102/// Check that the given argument, identified by its name and initialization103/// value, is present in the \c arguments `dag`.104static bool verifyArgument(const DagInit *arguments, StringRef argName,105                           const Init *argInit) {106  auto range = zip_equal(arguments->getArgNames(), arguments->getArgs());107  return llvm::any_of(108      range, [&](std::tuple<const llvm::StringInit *, const llvm::Init *> v) {109        return std::get<0>(v)->getAsUnquotedString() == argName &&110               std::get<1>(v) == argInit;111      });112}113 114/// Check that the given string record value, identified by its \c opValueName,115/// is either undefined or empty in both the given operation and clause record116/// or its contents for the clause record are contained in the operation record.117/// Passing a non-empty \c clauseValueName enables checking values named118/// differently in the operation and clause records.119static bool verifyStringValue(const Record *op, const Record *clause,120                              StringRef opValueName,121                              StringRef clauseValueName = {}) {122  auto opValue = op->getValueAsOptionalString(opValueName);123  auto clauseValue = clause->getValueAsOptionalString(124      clauseValueName.empty() ? opValueName : clauseValueName);125 126  bool opHasValue = opValue && !opValue->trim().empty();127  bool clauseHasValue = clauseValue && !clauseValue->trim().empty();128 129  if (!opHasValue)130    return !clauseHasValue;131 132  return !clauseHasValue || opValue->contains(clauseValue->trim());133}134 135/// Verify that all fields of the given clause not explicitly ignored are136/// present in the corresponding operation field.137///138/// Print warnings or errors where this is not the case.139static void verifyClause(const Record *op, const Record *clause) {140  StringRef clauseClassName = extractOmpClauseName(clause);141 142  if (!clause->getValueAsBit("ignoreArgs")) {143    const DagInit *opArguments = op->getValueAsDag("arguments");144    const DagInit *arguments = clause->getValueAsDag("arguments");145 146    for (auto [name, arg] :147         zip(arguments->getArgNames(), arguments->getArgs())) {148      if (!verifyArgument(opArguments, name->getAsUnquotedString(), arg))149        PrintWarning(150            op->getLoc(),151            "'" + clauseClassName + "' clause-defined argument '" +152                arg->getAsUnquotedString() + ":$" +153                name->getAsUnquotedString() +154                "' not present in operation. Consider `dag arguments = "155                "!con(clausesArgs, ...)` or explicitly skipping this field.");156    }157  }158 159  if (!clause->getValueAsBit("ignoreAsmFormat") &&160      !verifyStringValue(op, clause, "assemblyFormat", "reqAssemblyFormat"))161    PrintWarning(162        op->getLoc(),163        "'" + clauseClassName +164            "' clause-defined `reqAssemblyFormat` not present in operation. "165            "Consider concatenating `clauses[{Req,Opt}]AssemblyFormat` or "166            "explicitly skipping this field.");167 168  if (!clause->getValueAsBit("ignoreAsmFormat") &&169      !verifyStringValue(op, clause, "assemblyFormat", "optAssemblyFormat"))170    PrintWarning(171        op->getLoc(),172        "'" + clauseClassName +173            "' clause-defined `optAssemblyFormat` not present in operation. "174            "Consider concatenating `clauses[{Req,Opt}]AssemblyFormat` or "175            "explicitly skipping this field.");176 177  if (!clause->getValueAsBit("ignoreDesc") &&178      !verifyStringValue(op, clause, "description"))179    PrintError(op->getLoc(),180               "'" + clauseClassName +181                   "' clause-defined `description` not present in operation. "182                   "Consider concatenating `clausesDescription` or explicitly "183                   "skipping this field.");184 185  if (!clause->getValueAsBit("ignoreExtraDecl") &&186      !verifyStringValue(op, clause, "extraClassDeclaration"))187    PrintWarning(188        op->getLoc(),189        "'" + clauseClassName +190            "' clause-defined `extraClassDeclaration` not present in "191            "operation. Consider concatenating `clausesExtraClassDeclaration` "192            "or explicitly skipping this field.");193}194 195/// Translate the type of an OpenMP clause's argument to its corresponding196/// representation for clause operand structures.197///198/// All kinds of values are represented as `mlir::Value` fields, whereas199/// attributes are represented based on their `storageType`.200///201/// \param[in] name The name of the argument.202/// \param[in] init The `DefInit` object representing the argument.203/// \param[out] nest Number of levels of array nesting associated with the204///                  type. Must be initially set to 0.205/// \param[out] rank Rank (number of dimensions, if an array type) of the base206///                  type. Must be initially set to 1.207///208/// \return the name of the base type to represent elements of the argument209///         type.210static StringRef translateArgumentType(ArrayRef<SMLoc> loc,211                                       const StringInit *name, const Init *init,212                                       int &nest, int &rank) {213  const Record *def = cast<DefInit>(init)->getDef();214 215  llvm::StringSet<> superClasses;216  for (const Record *sc : def->getSuperClasses())217    superClasses.insert(sc->getNameInitAsString());218 219  // Handle wrapper-style superclasses.220  if (superClasses.contains("OptionalAttr"))221    return translateArgumentType(222        loc, name, def->getValue("baseAttr")->getValue(), nest, rank);223 224  if (superClasses.contains("TypedArrayAttrBase"))225    return translateArgumentType(226        loc, name, def->getValue("elementAttr")->getValue(), ++nest, rank);227 228  // Handle ElementsAttrBase superclasses.229  if (superClasses.contains("ElementsAttrBase")) {230    // TODO: Obtain the rank from ranked types.231    ++nest;232 233    if (superClasses.contains("IntElementsAttrBase"))234      return "::llvm::APInt";235    if (superClasses.contains("FloatElementsAttr") ||236        superClasses.contains("RankedFloatElementsAttr"))237      return "::llvm::APFloat";238    if (superClasses.contains("DenseArrayAttrBase"))239      return stripPrefixAndSuffix(def->getValueAsString("returnType"),240                                  {"::llvm::ArrayRef<"}, {">"});241 242    // Decrease the nesting depth in the case where the base type cannot be243    // inferred, so that the bare storageType is used instead of a vector.244    --nest;245    PrintWarning(246        loc,247        "could not infer array-like attribute element type for argument '" +248            name->getAsUnquotedString() + "', will use bare `storageType`");249  }250 251  // Handle simple attribute and value types.252  [[maybe_unused]] bool isAttr = superClasses.contains("Attr");253  bool isValue = superClasses.contains("TypeConstraint");254  if (superClasses.contains("Variadic"))255    ++nest;256 257  if (isValue) {258    assert(!isAttr &&259           "argument can't be simultaneously a value and an attribute");260    return "::mlir::Value";261  }262 263  assert(isAttr && "argument must be an attribute if it's not a value");264  return nest > 0 ? "::mlir::Attribute"265                  : def->getValueAsString("storageType").trim();266}267 268/// Generate the structure that represents the arguments of the given \c clause269/// record of type \c OpenMP_Clause.270///271/// It will contain a field for each argument, using the same name translated to272/// camel case and the corresponding base type as returned by273/// translateArgumentType() optionally wrapped in one or more llvm::SmallVector.274///275/// An additional field containing a tuple of integers to hold the size of each276/// dimension will also be created for multi-rank types. This is not yet277/// supported.278static void genClauseOpsStruct(const Record *clause, raw_ostream &os) {279  if (clause->isAnonymous())280    return;281 282  StringRef clauseName = extractOmpClauseName(clause);283  os << "struct " << clauseName << "ClauseOps {\n";284 285  const DagInit *arguments = clause->getValueAsDag("arguments");286  for (auto [name, arg] :287       zip_equal(arguments->getArgNames(), arguments->getArgs())) {288    int nest = 0, rank = 1;289    StringRef baseType =290        translateArgumentType(clause->getLoc(), name, arg, nest, rank);291    std::string fieldName =292        convertToCamelFromSnakeCase(name->getAsUnquotedString(),293                                    /*capitalizeFirst=*/false);294 295    os << formatv("  {0}{1}{2} {3};\n",296                  fmt_repeat("::llvm::SmallVector<", nest), baseType,297                  fmt_repeat(">", nest), fieldName);298 299    if (rank > 1) {300      assert(nest >= 1 && "must be nested if it's a ranked type");301      os << formatv("  {0}::std::tuple<{1}int>{2} {3}Dims;\n",302                    fmt_repeat("::llvm::SmallVector<", nest - 1),303                    fmt_repeat("int, ", rank - 1), fmt_repeat(">", nest - 1),304                    fieldName);305    }306  }307 308  os << "};\n";309}310 311/// Generate the structure that represents the clause-related arguments of the312/// given \c op record of type \c OpenMP_Op.313///314/// This structure will be defined in terms of the clause operand structures315/// associated to the clauses of the operation.316static void genOperandsDef(const Record *op, raw_ostream &os) {317  if (op->isAnonymous())318    return;319 320  SmallVector<std::string> clauseNames;321  for (const Record *clause : op->getValueAsListOfDefs("clauseList"))322    clauseNames.push_back((extractOmpClauseName(clause) + "ClauseOps").str());323 324  StringRef opName = stripPrefixAndSuffix(325      op->getName(), /*prefixes=*/{"OpenMP_"}, /*suffixes=*/{"Op"});326  os << formatv(operationArgStruct, opName, join(clauseNames, ", "));327}328 329/// Verify that all properties of `OpenMP_Clause`s of records deriving from330/// `OpenMP_Op`s have been inherited by the latter.331static bool verifyDecls(const RecordKeeper &records, raw_ostream &) {332  for (const Record *op : records.getAllDerivedDefinitions("OpenMP_Op")) {333    for (const Record *clause : op->getValueAsListOfDefs("clauseList"))334      verifyClause(op, clause);335  }336 337  return false;338}339 340/// Generate structures to represent clause-related operands, based on existing341/// `OpenMP_Clause` definitions and aggregate them into operation-specific342/// structures according to the `clauses` argument of each definition deriving343/// from `OpenMP_Op`.344static bool genClauseOps(const RecordKeeper &records, raw_ostream &os) {345  llvm::NamespaceEmitter ns(os, "mlir::omp");346  for (const Record *clause : records.getAllDerivedDefinitions("OpenMP_Clause"))347    genClauseOpsStruct(clause, os);348 349  // Produce base mixin class.350  os << baseMixinClass;351 352  for (const Record *op : records.getAllDerivedDefinitions("OpenMP_Op"))353    genOperandsDef(op, os);354 355  return false;356}357 358// Registers the generator to mlir-tblgen.359static mlir::GenRegistration360    verifyOpenmpOps("verify-openmp-ops",361                    "Verify OpenMP operations (produce no output file)",362                    verifyDecls);363 364static mlir::GenRegistration365    genOpenmpClauseOps("gen-openmp-clause-ops",366                       "Generate OpenMP clause operand structures",367                       genClauseOps);368