brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.4 KiB · 9ad031e Raw
479 lines · cpp
1//===- CodeGenHelpers.cpp - MLIR op definitions 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-exception6//7//===----------------------------------------------------------------------===//8//9// OpDefinitionsGen uses the description of operations to generate C++10// definitions for ops.11//12//===----------------------------------------------------------------------===//13 14#include "mlir/TableGen/CodeGenHelpers.h"15#include "mlir/Support/LLVM.h"16#include "mlir/TableGen/Argument.h"17#include "mlir/TableGen/Attribute.h"18#include "mlir/TableGen/Format.h"19#include "mlir/TableGen/Operator.h"20#include "mlir/TableGen/Pattern.h"21#include "mlir/TableGen/Property.h"22#include "mlir/TableGen/Region.h"23#include "mlir/TableGen/Successor.h"24#include "llvm/ADT/StringExtras.h"25#include "llvm/ADT/StringRef.h"26#include "llvm/Support/FormatVariadic.h"27#include "llvm/Support/Path.h"28#include "llvm/Support/raw_ostream.h"29#include "llvm/TableGen/CodeGenHelpers.h"30#include "llvm/TableGen/Error.h"31#include "llvm/TableGen/Record.h"32#include <cassert>33#include <optional>34#include <string>35 36using namespace llvm;37using namespace mlir;38using namespace mlir::tblgen;39 40/// Generate a unique label based on the current file name to prevent name41/// collisions if multiple generated files are included at once.42static std::string getUniqueOutputLabel(const RecordKeeper &records,43                                        StringRef tag) {44  // Use the input file name when generating a unique name.45  StringRef inputFilename = records.getInputFilename();46 47  // Drop all but the base filename.48  StringRef nameRef = sys::path::filename(inputFilename);49  nameRef.consume_back(".td");50 51  // Sanitize any invalid characters.52  std::string uniqueName(tag);53  for (char c : nameRef) {54    if (isAlnum(c) || c == '_')55      uniqueName.push_back(c);56    else57      uniqueName.append(utohexstr((unsigned char)c));58  }59  return uniqueName;60}61 62StaticVerifierFunctionEmitter::StaticVerifierFunctionEmitter(63    raw_ostream &os, const RecordKeeper &records, StringRef tag)64    : os(os), uniqueOutputLabel(getUniqueOutputLabel(records, tag)) {}65 66void StaticVerifierFunctionEmitter::emitOpConstraints() {67  emitTypeConstraints();68  emitAttrConstraints();69  emitPropConstraints();70  emitSuccessorConstraints();71  emitRegionConstraints();72}73 74void StaticVerifierFunctionEmitter::emitPatternConstraints(75    const ArrayRef<DagLeaf> constraints) {76  collectPatternConstraints(constraints);77  emitPatternConstraints();78}79 80//===----------------------------------------------------------------------===//81// Constraint Getters82//===----------------------------------------------------------------------===//83 84StringRef StaticVerifierFunctionEmitter::getTypeConstraintFn(85    const Constraint &constraint) const {86  const auto *it = typeConstraints.find(constraint);87  assert(it != typeConstraints.end() && "expected to find a type constraint");88  return it->second;89}90 91// Find a uniqued attribute constraint. Since not all attribute constraints can92// be uniqued, return std::nullopt if one was not found.93std::optional<StringRef> StaticVerifierFunctionEmitter::getAttrConstraintFn(94    const Constraint &constraint) const {95  const auto *it = attrConstraints.find(constraint);96  return it == attrConstraints.end() ? std::optional<StringRef>()97                                     : StringRef(it->second);98}99 100// Find a uniqued property constraint. Since not all property constraints can101// be uniqued, return std::nullopt if one was not found.102std::optional<StringRef> StaticVerifierFunctionEmitter::getPropConstraintFn(103    const Constraint &constraint) const {104  const auto *it = propConstraints.find(constraint);105  return it == propConstraints.end() ? std::optional<StringRef>()106                                     : StringRef(it->second);107}108 109StringRef StaticVerifierFunctionEmitter::getSuccessorConstraintFn(110    const Constraint &constraint) const {111  const auto *it = successorConstraints.find(constraint);112  assert(it != successorConstraints.end() &&113         "expected to find a sucessor constraint");114  return it->second;115}116 117StringRef StaticVerifierFunctionEmitter::getRegionConstraintFn(118    const Constraint &constraint) const {119  const auto *it = regionConstraints.find(constraint);120  assert(it != regionConstraints.end() &&121         "expected to find a region constraint");122  return it->second;123}124 125//===----------------------------------------------------------------------===//126// Constraint Emission127//===----------------------------------------------------------------------===//128 129/// Helper to generate a C++ string expression from a given message.130/// Message can contain '{{...}}' placeholders that are substituted with131/// C-expressions via tgfmt.132std::string mlir::tblgen::buildErrorStreamingString(133    StringRef message, const FmtContext &ctx, ErrorStreamType errorStreamType) {134  std::string result;135  raw_string_ostream os(result);136 137  std::string msgStr = escapeString(message);138  StringRef msg = msgStr;139 140  // Split the message by '{{' and '}}' and build a streaming expression.141  auto split = msg.split("{{");142  os << split.first;143  if (split.second.empty()) {144    return msgStr;145  }146 147  if (errorStreamType == ErrorStreamType::InsideOpError)148    os << "\")";149  else150    os << '"';151 152  msg = split.second;153  while (!msg.empty()) {154    split = msg.split("}}");155    StringRef var = split.first;156    StringRef rest = split.second;157 158    os << " << " << tgfmt(var, &ctx);159 160    if (rest.empty())161      break;162 163    split = rest.split("{{");164    if (split.second.empty() &&165        errorStreamType == ErrorStreamType::InsideOpError) {166      // To enable having part of string post, this adds a parenthesis before167      // the last string segment to match the existing one.168      os << " << (\"" << split.first;169    } else {170      os << " << \"" << split.first;171    }172    msg = split.second;173  }174 175  return os.str();176}177 178/// Code templates for emitting type, attribute, successor, and region179/// constraints. Each of these templates require the following arguments:180///181/// {0}: The unique constraint name.182/// {1}: The constraint code.183/// {2}: The constraint description.184 185/// Code for a type constraint. These may be called on the type of either186/// operands or results.187static const char *const typeConstraintCode = R"(188static ::llvm::LogicalResult {0}(189    ::mlir::Operation *op, ::mlir::Type type, ::llvm::StringRef valueKind,190    unsigned valueIndex) {191  if (!({1})) {192    return op->emitOpError(valueKind) << " #" << valueIndex193        << " must be {2}, but got " << type;194  }195  return ::mlir::success();196}197)";198 199/// Code for an attribute constraint. These may be called from ops only.200/// Attribute constraints cannot reference anything other than `$_self` and201/// `$_op`.202///203/// TODO: Unique constraints for adaptors. However, most Adaptor::verify204/// functions are stripped anyways.205static const char *const attrConstraintCode = R"(206static ::llvm::LogicalResult {0}(207    ::mlir::Attribute attr, ::llvm::StringRef attrName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{208  if (attr && !({1}))209    return emitError() << "attribute '" << attrName210        << "' failed to satisfy constraint: {2}";211  return ::mlir::success();212}213static ::llvm::LogicalResult {0}(214    ::mlir::Operation *op, ::mlir::Attribute attr, ::llvm::StringRef attrName) {{215  return {0}(attr, attrName, [op]() {{216    return op->emitOpError();217  });218}219)";220 221/// Code for a property constraint. These may be called from ops only.222/// Property constraints cannot reference anything other than `$_self` and223/// `$_op`. {3} is the interface type of the property.224static const char *const propConstraintCode = R"(225  static ::llvm::LogicalResult {0}(226      {3} prop, ::llvm::StringRef propName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{227    if (!({1}))228      return emitError() << "property '" << propName229          << "' failed to satisfy constraint: {2}";230    return ::mlir::success();231  }232  static ::llvm::LogicalResult {0}(233      ::mlir::Operation *op, {3} prop, ::llvm::StringRef propName) {{234    return {0}(prop, propName, [op]() {{235      return op->emitOpError();236    });237  }238  )";239 240/// Code for a successor constraint.241static const char *const successorConstraintCode = R"(242static ::llvm::LogicalResult {0}(243    ::mlir::Operation *op, ::mlir::Block *successor,244    ::llvm::StringRef successorName, unsigned successorIndex) {245  if (!({1})) {246    return op->emitOpError("successor #") << successorIndex << " ('"247        << successorName << ")' failed to verify constraint: {2}";248  }249  return ::mlir::success();250}251)";252 253/// Code for a region constraint. Callers will need to pass in the region's name254/// for emitting an error message.255static const char *const regionConstraintCode = R"(256static ::llvm::LogicalResult {0}(257    ::mlir::Operation *op, ::mlir::Region &region, ::llvm::StringRef regionName,258    unsigned regionIndex) {259  if (!({1})) {260    return op->emitOpError("region #") << regionIndex261        << (regionName.empty() ? " " : " ('" + regionName + "') ")262        << "failed to verify constraint: {2}";263  }264  return ::mlir::success();265}266)";267 268/// Code for a pattern type or attribute constraint.269///270/// {0}: name of function271/// {1}: Condition template272/// {2}: Constraint summary273/// {3}: "::mlir::Type type" or "::mlirAttribute attr" or "propType prop".274/// Can be "T prop" for generic property constraints.275static const char *const patternConstraintCode = R"(276static ::llvm::LogicalResult {0}(277    ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, {3},278    ::llvm::StringRef failureStr) {279  if (!({1})) {280    return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) {281      diag << failureStr << ": {2}";282    });283  }284  return ::mlir::success();285}286)";287 288void StaticVerifierFunctionEmitter::emitConstraints(289    const ConstraintMap &constraints, StringRef selfName,290    const char *const codeTemplate, ErrorStreamType errorStreamType) {291  FmtContext ctx;292  ctx.addSubst("_op", "*op").withSelf(selfName);293 294  for (auto &it : constraints) {295    os << formatv(codeTemplate, it.second,296                  tgfmt(it.first.getConditionTemplate(), &ctx),297                  buildErrorStreamingString(it.first.getSummary(), ctx));298  }299}300void StaticVerifierFunctionEmitter::emitTypeConstraints() {301  emitConstraints(typeConstraints, "type", typeConstraintCode,302                  ErrorStreamType::InString);303}304 305void StaticVerifierFunctionEmitter::emitAttrConstraints() {306  emitConstraints(attrConstraints, "attr", attrConstraintCode,307                  ErrorStreamType::InString);308}309 310/// Unlike with the other helpers, this one has to substitute in the interface311/// type of the property, so we can't just use the generic function.312void StaticVerifierFunctionEmitter::emitPropConstraints() {313  FmtContext ctx;314  ctx.addSubst("_op", "*op").withSelf("prop");315  for (auto &it : propConstraints) {316    auto propConstraint = cast<PropConstraint>(it.first);317    os << formatv(propConstraintCode, it.second,318                  tgfmt(propConstraint.getConditionTemplate(), &ctx),319                  buildErrorStreamingString(it.first.getSummary(), ctx),320                  propConstraint.getInterfaceType());321  }322}323 324void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {325  emitConstraints(successorConstraints, "successor", successorConstraintCode,326                  ErrorStreamType::InString);327}328 329void StaticVerifierFunctionEmitter::emitRegionConstraints() {330  emitConstraints(regionConstraints, "region", regionConstraintCode,331                  ErrorStreamType::InString);332}333 334void StaticVerifierFunctionEmitter::emitPatternConstraints() {335  FmtContext ctx;336  ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type");337  for (auto &it : typeConstraints) {338    os << formatv(patternConstraintCode, it.second,339                  tgfmt(it.first.getConditionTemplate(), &ctx),340                  buildErrorStreamingString(it.first.getSummary(), ctx),341                  "::mlir::Type type");342  }343  ctx.withSelf("attr");344  for (auto &it : attrConstraints) {345    os << formatv(patternConstraintCode, it.second,346                  tgfmt(it.first.getConditionTemplate(), &ctx),347                  buildErrorStreamingString(it.first.getSummary(), ctx),348                  "::mlir::Attribute attr");349  }350  ctx.withSelf("prop");351  for (auto &it : propConstraints) {352    PropConstraint propConstraint = cast<PropConstraint>(it.first);353    StringRef interfaceType = propConstraint.getInterfaceType();354    // Constraints that are generic over multiple interface types are355    // templatized under the assumption that they'll be used correctly.356    if (interfaceType.empty()) {357      interfaceType = "T";358      os << "template <typename T>";359    }360    os << formatv(patternConstraintCode, it.second,361                  tgfmt(propConstraint.getConditionTemplate(), &ctx),362                  buildErrorStreamingString(propConstraint.getSummary(), ctx),363                  Twine(interfaceType) + " prop");364  }365}366 367//===----------------------------------------------------------------------===//368// Constraint Uniquing369//===----------------------------------------------------------------------===//370 371/// An attribute constraint that references anything other than itself and the372/// current op cannot be generically extracted into a function. Most373/// prohibitive are operands and results, which require calls to374/// `getODSOperands` or `getODSResults`. Attribute references are tricky too375/// because ops use cached identifiers.376static bool canUniqueAttrConstraint(Attribute attr) {377  FmtContext ctx;378  auto test = tgfmt(attr.getConditionTemplate(),379                    &ctx.withSelf("attr").addSubst("_op", "*op"))380                  .str();381  return !StringRef(test).contains("<no-subst-found>");382}383 384/// A property constraint that references anything other than itself and the385/// current op cannot be generically extracted into a function, just as with386/// canUnequePropConstraint(). Additionally, property constraints without387/// an interface type specified can't be uniqued, and ones that are a literal388/// "true" shouldn't be constrained.389static bool canUniquePropConstraint(Property prop) {390  FmtContext ctx;391  auto test = tgfmt(prop.getConditionTemplate(),392                    &ctx.withSelf("prop").addSubst("_op", "*op"))393                  .str();394  return !StringRef(test).contains("<no-subst-found>") && test != "true" &&395         !prop.getInterfaceType().empty();396}397 398std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind,399                                                         unsigned index) {400  return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel +401          Twine(index))402      .str();403}404 405void StaticVerifierFunctionEmitter::collectConstraint(ConstraintMap &map,406                                                      StringRef kind,407                                                      Constraint constraint) {408  auto [it, inserted] = map.try_emplace(constraint);409  if (inserted)410    it->second = getUniqueName(kind, map.size());411}412 413void StaticVerifierFunctionEmitter::collectOpConstraints(414    ArrayRef<const Record *> opDefs) {415  const auto collectTypeConstraints = [&](Operator::const_value_range values) {416    for (const NamedTypeConstraint &value : values)417      if (value.hasPredicate())418        collectConstraint(typeConstraints, "type", value.constraint);419  };420 421  for (const Record *def : opDefs) {422    Operator op(*def);423    /// Collect type constraints.424    collectTypeConstraints(op.getOperands());425    collectTypeConstraints(op.getResults());426    /// Collect attribute constraints.427    for (const NamedAttribute &namedAttr : op.getAttributes()) {428      if (!namedAttr.attr.getPredicate().isNull() &&429          !namedAttr.attr.isDerivedAttr() &&430          canUniqueAttrConstraint(namedAttr.attr))431        collectConstraint(attrConstraints, "attr", namedAttr.attr);432    }433    /// Collect non-trivial property constraints.434    for (const NamedProperty &namedProp : op.getProperties()) {435      if (!namedProp.prop.getPredicate().isNull() &&436          canUniquePropConstraint(namedProp.prop)) {437        collectConstraint(propConstraints, "prop", namedProp.prop);438      }439    }440    /// Collect successor constraints.441    for (const NamedSuccessor &successor : op.getSuccessors()) {442      if (!successor.constraint.getPredicate().isNull()) {443        collectConstraint(successorConstraints, "successor",444                          successor.constraint);445      }446    }447    /// Collect region constraints.448    for (const NamedRegion &region : op.getRegions())449      if (!region.constraint.getPredicate().isNull())450        collectConstraint(regionConstraints, "region", region.constraint);451  }452}453 454void StaticVerifierFunctionEmitter::collectPatternConstraints(455    const ArrayRef<DagLeaf> constraints) {456  for (auto &leaf : constraints) {457    assert(leaf.isOperandMatcher() || leaf.isAttrMatcher() ||458           leaf.isPropMatcher());459    Constraint constraint = leaf.getAsConstraint();460    if (leaf.isOperandMatcher())461      collectConstraint(typeConstraints, "type", constraint);462    else if (leaf.isAttrMatcher())463      collectConstraint(attrConstraints, "attr", constraint);464    else if (leaf.isPropMatcher())465      collectConstraint(propConstraints, "prop", constraint);466  }467}468 469//===----------------------------------------------------------------------===//470// Public Utility Functions471//===----------------------------------------------------------------------===//472 473std::string mlir::tblgen::escapeString(StringRef value) {474  std::string ret;475  raw_string_ostream os(ret);476  os.write_escaped(value);477  return ret;478}479