1f570cc17SMarkus Böck //===- CodeGenHelpers.cpp - MLIR op definitions generator ---------------===// 2f570cc17SMarkus Böck // 3f570cc17SMarkus Böck // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4f570cc17SMarkus Böck // See https://llvm.org/LICENSE.txt for license information. 5f570cc17SMarkus Böck // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6f570cc17SMarkus Böck // 7f570cc17SMarkus Böck //===----------------------------------------------------------------------===// 8f570cc17SMarkus Böck // 9f570cc17SMarkus Böck // OpDefinitionsGen uses the description of operations to generate C++ 10f570cc17SMarkus Böck // definitions for ops. 11f570cc17SMarkus Böck // 12f570cc17SMarkus Böck //===----------------------------------------------------------------------===// 13f570cc17SMarkus Böck 14f570cc17SMarkus Böck #include "mlir/TableGen/CodeGenHelpers.h" 15f570cc17SMarkus Böck #include "mlir/TableGen/Operator.h" 16f570cc17SMarkus Böck #include "mlir/TableGen/Pattern.h" 17f570cc17SMarkus Böck #include "llvm/Support/FormatVariadic.h" 18f570cc17SMarkus Böck #include "llvm/Support/Path.h" 19f570cc17SMarkus Böck #include "llvm/TableGen/Record.h" 20f570cc17SMarkus Böck 21f570cc17SMarkus Böck using namespace llvm; 22f570cc17SMarkus Böck using namespace mlir; 23f570cc17SMarkus Böck using namespace mlir::tblgen; 24f570cc17SMarkus Böck 25f570cc17SMarkus Böck /// Generate a unique label based on the current file name to prevent name 26f570cc17SMarkus Böck /// collisions if multiple generated files are included at once. 27*659192b1SRahul Joshi static std::string getUniqueOutputLabel(const RecordKeeper &records, 281b232fa0SJeff Niu StringRef tag) { 29f570cc17SMarkus Böck // Use the input file name when generating a unique name. 30f570cc17SMarkus Böck std::string inputFilename = records.getInputFilename(); 31f570cc17SMarkus Böck 32f570cc17SMarkus Böck // Drop all but the base filename. 33*659192b1SRahul Joshi StringRef nameRef = sys::path::filename(inputFilename); 34f570cc17SMarkus Böck nameRef.consume_back(".td"); 35f570cc17SMarkus Böck 36f570cc17SMarkus Böck // Sanitize any invalid characters. 371b232fa0SJeff Niu std::string uniqueName(tag); 38f570cc17SMarkus Böck for (char c : nameRef) { 39*659192b1SRahul Joshi if (isAlnum(c) || c == '_') 40f570cc17SMarkus Böck uniqueName.push_back(c); 41f570cc17SMarkus Böck else 42*659192b1SRahul Joshi uniqueName.append(utohexstr((unsigned char)c)); 43f570cc17SMarkus Böck } 44f570cc17SMarkus Böck return uniqueName; 45f570cc17SMarkus Böck } 46f570cc17SMarkus Böck 47f570cc17SMarkus Böck StaticVerifierFunctionEmitter::StaticVerifierFunctionEmitter( 48*659192b1SRahul Joshi raw_ostream &os, const RecordKeeper &records, StringRef tag) 491b232fa0SJeff Niu : os(os), uniqueOutputLabel(getUniqueOutputLabel(records, tag)) {} 50f570cc17SMarkus Böck 51f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitOpConstraints( 52*659192b1SRahul Joshi ArrayRef<const Record *> opDefs) { 53f570cc17SMarkus Böck NamespaceEmitter namespaceEmitter(os, Operator(*opDefs[0]).getCppNamespace()); 54f570cc17SMarkus Böck emitTypeConstraints(); 55f570cc17SMarkus Böck emitAttrConstraints(); 56f570cc17SMarkus Böck emitSuccessorConstraints(); 57f570cc17SMarkus Böck emitRegionConstraints(); 58f570cc17SMarkus Böck } 59f570cc17SMarkus Böck 60f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitPatternConstraints( 61*659192b1SRahul Joshi const ArrayRef<DagLeaf> constraints) { 62f570cc17SMarkus Böck collectPatternConstraints(constraints); 63f570cc17SMarkus Böck emitPatternConstraints(); 64f570cc17SMarkus Böck } 65f570cc17SMarkus Böck 66f570cc17SMarkus Böck //===----------------------------------------------------------------------===// 67f570cc17SMarkus Böck // Constraint Getters 68f570cc17SMarkus Böck 69f570cc17SMarkus Böck StringRef StaticVerifierFunctionEmitter::getTypeConstraintFn( 70f570cc17SMarkus Böck const Constraint &constraint) const { 71dfe17596SMehdi Amini const auto *it = typeConstraints.find(constraint); 72f570cc17SMarkus Böck assert(it != typeConstraints.end() && "expected to find a type constraint"); 73f570cc17SMarkus Böck return it->second; 74f570cc17SMarkus Böck } 75f570cc17SMarkus Böck 76f570cc17SMarkus Böck // Find a uniqued attribute constraint. Since not all attribute constraints can 7770c73d1bSKazu Hirata // be uniqued, return std::nullopt if one was not found. 783cfe412eSFangrui Song std::optional<StringRef> StaticVerifierFunctionEmitter::getAttrConstraintFn( 79f570cc17SMarkus Böck const Constraint &constraint) const { 80dfe17596SMehdi Amini const auto *it = attrConstraints.find(constraint); 813cfe412eSFangrui Song return it == attrConstraints.end() ? std::optional<StringRef>() 82f570cc17SMarkus Böck : StringRef(it->second); 83f570cc17SMarkus Böck } 84f570cc17SMarkus Böck 85f570cc17SMarkus Böck StringRef StaticVerifierFunctionEmitter::getSuccessorConstraintFn( 86f570cc17SMarkus Böck const Constraint &constraint) const { 87dfe17596SMehdi Amini const auto *it = successorConstraints.find(constraint); 88f570cc17SMarkus Böck assert(it != successorConstraints.end() && 89f570cc17SMarkus Böck "expected to find a sucessor constraint"); 90f570cc17SMarkus Böck return it->second; 91f570cc17SMarkus Böck } 92f570cc17SMarkus Böck 93f570cc17SMarkus Böck StringRef StaticVerifierFunctionEmitter::getRegionConstraintFn( 94f570cc17SMarkus Böck const Constraint &constraint) const { 95dfe17596SMehdi Amini const auto *it = regionConstraints.find(constraint); 96f570cc17SMarkus Böck assert(it != regionConstraints.end() && 97f570cc17SMarkus Böck "expected to find a region constraint"); 98f570cc17SMarkus Böck return it->second; 99f570cc17SMarkus Böck } 100f570cc17SMarkus Böck 101f570cc17SMarkus Böck //===----------------------------------------------------------------------===// 102f570cc17SMarkus Böck // Constraint Emission 103f570cc17SMarkus Böck 104f570cc17SMarkus Böck /// Code templates for emitting type, attribute, successor, and region 105f570cc17SMarkus Böck /// constraints. Each of these templates require the following arguments: 106f570cc17SMarkus Böck /// 107f570cc17SMarkus Böck /// {0}: The unique constraint name. 108f570cc17SMarkus Böck /// {1}: The constraint code. 109f570cc17SMarkus Böck /// {2}: The constraint description. 110f570cc17SMarkus Böck 111f570cc17SMarkus Böck /// Code for a type constraint. These may be called on the type of either 112f570cc17SMarkus Böck /// operands or results. 113f570cc17SMarkus Böck static const char *const typeConstraintCode = R"( 114db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}( 115f570cc17SMarkus Böck ::mlir::Operation *op, ::mlir::Type type, ::llvm::StringRef valueKind, 116f570cc17SMarkus Böck unsigned valueIndex) { 117f570cc17SMarkus Böck if (!({1})) { 118f570cc17SMarkus Böck return op->emitOpError(valueKind) << " #" << valueIndex 119f570cc17SMarkus Böck << " must be {2}, but got " << type; 120f570cc17SMarkus Böck } 121f570cc17SMarkus Böck return ::mlir::success(); 122f570cc17SMarkus Böck } 123f570cc17SMarkus Böck )"; 124f570cc17SMarkus Böck 125f570cc17SMarkus Böck /// Code for an attribute constraint. These may be called from ops only. 126f570cc17SMarkus Böck /// Attribute constraints cannot reference anything other than `$_self` and 127f570cc17SMarkus Böck /// `$_op`. 128f570cc17SMarkus Böck /// 129f570cc17SMarkus Böck /// TODO: Unique constraints for adaptors. However, most Adaptor::verify 130f570cc17SMarkus Böck /// functions are stripped anyways. 131f570cc17SMarkus Böck static const char *const attrConstraintCode = R"( 132db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}( 133c50617daSMehdi Amini ::mlir::Attribute attr, ::llvm::StringRef attrName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{ 1345e118f93SMehdi Amini if (attr && !({1})) 135c50617daSMehdi Amini return emitError() << "attribute '" << attrName 136f570cc17SMarkus Böck << "' failed to satisfy constraint: {2}"; 1371e853421SMehdi Amini return ::mlir::success(); 138d572cd1bSMehdi Amini } 139db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}( 1405e118f93SMehdi Amini ::mlir::Operation *op, ::mlir::Attribute attr, ::llvm::StringRef attrName) {{ 1415e118f93SMehdi Amini return {0}(attr, attrName, [op]() {{ 1425e118f93SMehdi Amini return op->emitOpError(); 1435e118f93SMehdi Amini }); 1445e118f93SMehdi Amini } 145f570cc17SMarkus Böck )"; 146f570cc17SMarkus Böck 147f570cc17SMarkus Böck /// Code for a successor constraint. 148f570cc17SMarkus Böck static const char *const successorConstraintCode = R"( 149db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}( 150f570cc17SMarkus Böck ::mlir::Operation *op, ::mlir::Block *successor, 151f570cc17SMarkus Böck ::llvm::StringRef successorName, unsigned successorIndex) { 152f570cc17SMarkus Böck if (!({1})) { 153f570cc17SMarkus Böck return op->emitOpError("successor #") << successorIndex << " ('" 154f570cc17SMarkus Böck << successorName << ")' failed to verify constraint: {2}"; 155f570cc17SMarkus Böck } 156f570cc17SMarkus Böck return ::mlir::success(); 157f570cc17SMarkus Böck } 158f570cc17SMarkus Böck )"; 159f570cc17SMarkus Böck 160f570cc17SMarkus Böck /// Code for a region constraint. Callers will need to pass in the region's name 161f570cc17SMarkus Böck /// for emitting an error message. 162f570cc17SMarkus Böck static const char *const regionConstraintCode = R"( 163db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}( 164f570cc17SMarkus Böck ::mlir::Operation *op, ::mlir::Region ®ion, ::llvm::StringRef regionName, 165f570cc17SMarkus Böck unsigned regionIndex) { 166f570cc17SMarkus Böck if (!({1})) { 167f570cc17SMarkus Böck return op->emitOpError("region #") << regionIndex 168f570cc17SMarkus Böck << (regionName.empty() ? " " : " ('" + regionName + "') ") 169f570cc17SMarkus Böck << "failed to verify constraint: {2}"; 170f570cc17SMarkus Böck } 171f570cc17SMarkus Böck return ::mlir::success(); 172f570cc17SMarkus Böck } 173f570cc17SMarkus Böck )"; 174f570cc17SMarkus Böck 175f570cc17SMarkus Böck /// Code for a pattern type or attribute constraint. 176f570cc17SMarkus Böck /// 177f570cc17SMarkus Böck /// {3}: "Type type" or "Attribute attr". 178f570cc17SMarkus Böck static const char *const patternAttrOrTypeConstraintCode = R"( 179db791b27SRamkumar Ramachandra static ::llvm::LogicalResult {0}( 180f570cc17SMarkus Böck ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, ::mlir::{3}, 181f570cc17SMarkus Böck ::llvm::StringRef failureStr) { 182f570cc17SMarkus Böck if (!({1})) { 183f570cc17SMarkus Böck return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) { 184f570cc17SMarkus Böck diag << failureStr << ": {2}"; 185f570cc17SMarkus Böck }); 186f570cc17SMarkus Böck } 187f570cc17SMarkus Böck return ::mlir::success(); 188f570cc17SMarkus Böck } 189f570cc17SMarkus Böck )"; 190f570cc17SMarkus Böck 191f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitConstraints( 192f570cc17SMarkus Böck const ConstraintMap &constraints, StringRef selfName, 193f570cc17SMarkus Böck const char *const codeTemplate) { 194f570cc17SMarkus Böck FmtContext ctx; 1955cdc2bbcSRiver Riddle ctx.addSubst("_op", "*op").withSelf(selfName); 196f570cc17SMarkus Böck for (auto &it : constraints) { 197f570cc17SMarkus Böck os << formatv(codeTemplate, it.second, 198f570cc17SMarkus Böck tgfmt(it.first.getConditionTemplate(), &ctx), 199f570cc17SMarkus Böck escapeString(it.first.getSummary())); 200f570cc17SMarkus Böck } 201f570cc17SMarkus Böck } 202f570cc17SMarkus Böck 203f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitTypeConstraints() { 204f570cc17SMarkus Böck emitConstraints(typeConstraints, "type", typeConstraintCode); 205f570cc17SMarkus Böck } 206f570cc17SMarkus Böck 207f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitAttrConstraints() { 208f570cc17SMarkus Böck emitConstraints(attrConstraints, "attr", attrConstraintCode); 209f570cc17SMarkus Böck } 210f570cc17SMarkus Böck 211f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitSuccessorConstraints() { 212f570cc17SMarkus Böck emitConstraints(successorConstraints, "successor", successorConstraintCode); 213f570cc17SMarkus Böck } 214f570cc17SMarkus Böck 215f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitRegionConstraints() { 216f570cc17SMarkus Böck emitConstraints(regionConstraints, "region", regionConstraintCode); 217f570cc17SMarkus Böck } 218f570cc17SMarkus Böck 219f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::emitPatternConstraints() { 220f570cc17SMarkus Böck FmtContext ctx; 2215cdc2bbcSRiver Riddle ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type"); 222f570cc17SMarkus Böck for (auto &it : typeConstraints) { 223f570cc17SMarkus Böck os << formatv(patternAttrOrTypeConstraintCode, it.second, 224f570cc17SMarkus Böck tgfmt(it.first.getConditionTemplate(), &ctx), 225f570cc17SMarkus Böck escapeString(it.first.getSummary()), "Type type"); 226f570cc17SMarkus Böck } 227f570cc17SMarkus Böck ctx.withSelf("attr"); 228f570cc17SMarkus Böck for (auto &it : attrConstraints) { 229f570cc17SMarkus Böck os << formatv(patternAttrOrTypeConstraintCode, it.second, 230f570cc17SMarkus Böck tgfmt(it.first.getConditionTemplate(), &ctx), 231f570cc17SMarkus Böck escapeString(it.first.getSummary()), "Attribute attr"); 232f570cc17SMarkus Böck } 233f570cc17SMarkus Böck } 234f570cc17SMarkus Böck 235f570cc17SMarkus Böck //===----------------------------------------------------------------------===// 236f570cc17SMarkus Böck // Constraint Uniquing 237f570cc17SMarkus Böck 238f570cc17SMarkus Böck /// An attribute constraint that references anything other than itself and the 239f570cc17SMarkus Böck /// current op cannot be generically extracted into a function. Most 240f570cc17SMarkus Böck /// prohibitive are operands and results, which require calls to 241f570cc17SMarkus Böck /// `getODSOperands` or `getODSResults`. Attribute references are tricky too 242f570cc17SMarkus Böck /// because ops use cached identifiers. 243f570cc17SMarkus Böck static bool canUniqueAttrConstraint(Attribute attr) { 244f570cc17SMarkus Böck FmtContext ctx; 2455cdc2bbcSRiver Riddle auto test = tgfmt(attr.getConditionTemplate(), 2465cdc2bbcSRiver Riddle &ctx.withSelf("attr").addSubst("_op", "*op")) 247f570cc17SMarkus Böck .str(); 248f570cc17SMarkus Böck return !StringRef(test).contains("<no-subst-found>"); 249f570cc17SMarkus Böck } 250f570cc17SMarkus Böck 251f570cc17SMarkus Böck std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind, 252f570cc17SMarkus Böck unsigned index) { 253f570cc17SMarkus Böck return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel + 254f570cc17SMarkus Böck Twine(index)) 255f570cc17SMarkus Böck .str(); 256f570cc17SMarkus Böck } 257f570cc17SMarkus Böck 258f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::collectConstraint(ConstraintMap &map, 259f570cc17SMarkus Böck StringRef kind, 260f570cc17SMarkus Böck Constraint constraint) { 26118b39497SKazu Hirata auto [it, inserted] = map.try_emplace(constraint); 26218b39497SKazu Hirata if (inserted) 26318b39497SKazu Hirata it->second = getUniqueName(kind, map.size()); 264f570cc17SMarkus Böck } 265f570cc17SMarkus Böck 266f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::collectOpConstraints( 267b60c6cbcSRahul Joshi ArrayRef<const Record *> opDefs) { 268f570cc17SMarkus Böck const auto collectTypeConstraints = [&](Operator::const_value_range values) { 269f570cc17SMarkus Böck for (const NamedTypeConstraint &value : values) 270f570cc17SMarkus Böck if (value.hasPredicate()) 271f570cc17SMarkus Böck collectConstraint(typeConstraints, "type", value.constraint); 272f570cc17SMarkus Böck }; 273f570cc17SMarkus Böck 274b60c6cbcSRahul Joshi for (const Record *def : opDefs) { 275f570cc17SMarkus Böck Operator op(*def); 276f570cc17SMarkus Böck /// Collect type constraints. 277f570cc17SMarkus Böck collectTypeConstraints(op.getOperands()); 278f570cc17SMarkus Böck collectTypeConstraints(op.getResults()); 279f570cc17SMarkus Böck /// Collect attribute constraints. 280f570cc17SMarkus Böck for (const NamedAttribute &namedAttr : op.getAttributes()) { 281f570cc17SMarkus Böck if (!namedAttr.attr.getPredicate().isNull() && 282f570cc17SMarkus Böck !namedAttr.attr.isDerivedAttr() && 283f570cc17SMarkus Böck canUniqueAttrConstraint(namedAttr.attr)) 284f570cc17SMarkus Böck collectConstraint(attrConstraints, "attr", namedAttr.attr); 285f570cc17SMarkus Böck } 286f570cc17SMarkus Böck /// Collect successor constraints. 287f570cc17SMarkus Böck for (const NamedSuccessor &successor : op.getSuccessors()) { 288f570cc17SMarkus Böck if (!successor.constraint.getPredicate().isNull()) { 289f570cc17SMarkus Böck collectConstraint(successorConstraints, "successor", 290f570cc17SMarkus Böck successor.constraint); 291f570cc17SMarkus Böck } 292f570cc17SMarkus Böck } 293f570cc17SMarkus Böck /// Collect region constraints. 294f570cc17SMarkus Böck for (const NamedRegion ®ion : op.getRegions()) 295f570cc17SMarkus Böck if (!region.constraint.getPredicate().isNull()) 296f570cc17SMarkus Böck collectConstraint(regionConstraints, "region", region.constraint); 297f570cc17SMarkus Böck } 298f570cc17SMarkus Böck } 299f570cc17SMarkus Böck 300f570cc17SMarkus Böck void StaticVerifierFunctionEmitter::collectPatternConstraints( 301*659192b1SRahul Joshi const ArrayRef<DagLeaf> constraints) { 302f570cc17SMarkus Böck for (auto &leaf : constraints) { 303f570cc17SMarkus Böck assert(leaf.isOperandMatcher() || leaf.isAttrMatcher()); 304f570cc17SMarkus Böck collectConstraint( 305f570cc17SMarkus Böck leaf.isOperandMatcher() ? typeConstraints : attrConstraints, 306f570cc17SMarkus Böck leaf.isOperandMatcher() ? "type" : "attr", leaf.getAsConstraint()); 307f570cc17SMarkus Böck } 308f570cc17SMarkus Böck } 309f570cc17SMarkus Böck 310f570cc17SMarkus Böck //===----------------------------------------------------------------------===// 311f570cc17SMarkus Böck // Public Utility Functions 312f570cc17SMarkus Böck //===----------------------------------------------------------------------===// 313f570cc17SMarkus Böck 314f570cc17SMarkus Böck std::string mlir::tblgen::escapeString(StringRef value) { 315f570cc17SMarkus Böck std::string ret; 316*659192b1SRahul Joshi raw_string_ostream os(ret); 317f570cc17SMarkus Böck os.write_escaped(value); 318095b41c6SJOE1994 return ret; 319f570cc17SMarkus Böck } 320