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 ®ion, ::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 ®ion : 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