1 //===- RewriterGen.cpp - MLIR pattern rewriter 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // RewriterGen uses pattern rewrite definitions to generate rewriter matchers. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "mlir/Support/STLExtras.h" 14 #include "mlir/TableGen/Attribute.h" 15 #include "mlir/TableGen/Format.h" 16 #include "mlir/TableGen/GenInfo.h" 17 #include "mlir/TableGen/Operator.h" 18 #include "mlir/TableGen/Pattern.h" 19 #include "mlir/TableGen/Predicate.h" 20 #include "mlir/TableGen/Type.h" 21 #include "llvm/ADT/StringExtras.h" 22 #include "llvm/ADT/StringSet.h" 23 #include "llvm/Support/CommandLine.h" 24 #include "llvm/Support/Debug.h" 25 #include "llvm/Support/FormatAdapters.h" 26 #include "llvm/Support/PrettyStackTrace.h" 27 #include "llvm/Support/Signals.h" 28 #include "llvm/TableGen/Error.h" 29 #include "llvm/TableGen/Main.h" 30 #include "llvm/TableGen/Record.h" 31 #include "llvm/TableGen/TableGenBackend.h" 32 33 using namespace mlir; 34 using namespace mlir::tblgen; 35 36 using llvm::formatv; 37 using llvm::Record; 38 using llvm::RecordKeeper; 39 40 #define DEBUG_TYPE "mlir-tblgen-rewritergen" 41 42 namespace llvm { 43 template <> struct format_provider<mlir::tblgen::Pattern::IdentifierLine> { 44 static void format(const mlir::tblgen::Pattern::IdentifierLine &v, 45 raw_ostream &os, StringRef style) { 46 os << v.first << ":" << v.second; 47 } 48 }; 49 } // end namespace llvm 50 51 //===----------------------------------------------------------------------===// 52 // PatternEmitter 53 //===----------------------------------------------------------------------===// 54 55 namespace { 56 class PatternEmitter { 57 public: 58 PatternEmitter(Record *pat, RecordOperatorMap *mapper, raw_ostream &os); 59 60 // Emits the mlir::RewritePattern struct named `rewriteName`. 61 void emit(StringRef rewriteName); 62 63 private: 64 // Emits the code for matching ops. 65 void emitMatchLogic(DagNode tree); 66 67 // Emits the code for rewriting ops. 68 void emitRewriteLogic(); 69 70 //===--------------------------------------------------------------------===// 71 // Match utilities 72 //===--------------------------------------------------------------------===// 73 74 // Emits C++ statements for matching the op constrained by the given DAG 75 // `tree`. 76 void emitOpMatch(DagNode tree, int depth); 77 78 // Emits C++ statements for matching the `argIndex`-th argument of the given 79 // DAG `tree` as an operand. 80 void emitOperandMatch(DagNode tree, int argIndex, int depth, int indent); 81 82 // Emits C++ statements for matching the `argIndex`-th argument of the given 83 // DAG `tree` as an attribute. 84 void emitAttributeMatch(DagNode tree, int argIndex, int depth, int indent); 85 86 // Emits C++ for checking a match with a corresponding match failure 87 // diagnostic. 88 void emitMatchCheck(int depth, const FmtObjectBase &matchFmt, 89 const llvm::formatv_object_base &failureFmt); 90 91 //===--------------------------------------------------------------------===// 92 // Rewrite utilities 93 //===--------------------------------------------------------------------===// 94 95 // The entry point for handling a result pattern rooted at `resultTree`. This 96 // method dispatches to concrete handlers according to `resultTree`'s kind and 97 // returns a symbol representing the whole value pack. Callers are expected to 98 // further resolve the symbol according to the specific use case. 99 // 100 // `depth` is the nesting level of `resultTree`; 0 means top-level result 101 // pattern. For top-level result pattern, `resultIndex` indicates which result 102 // of the matched root op this pattern is intended to replace, which can be 103 // used to deduce the result type of the op generated from this result 104 // pattern. 105 std::string handleResultPattern(DagNode resultTree, int resultIndex, 106 int depth); 107 108 // Emits the C++ statement to replace the matched DAG with a value built via 109 // calling native C++ code. 110 std::string handleReplaceWithNativeCodeCall(DagNode resultTree); 111 112 // Returns the C++ expression referencing the old value serving as the 113 // replacement. 114 std::string handleReplaceWithValue(DagNode tree); 115 116 // Emits the C++ statement to build a new op out of the given DAG `tree` and 117 // returns the variable name that this op is assigned to. If the root op in 118 // DAG `tree` has a specified name, the created op will be assigned to a 119 // variable of the given name. Otherwise, a unique name will be used as the 120 // result value name. 121 std::string handleOpCreation(DagNode tree, int resultIndex, int depth); 122 123 using ChildNodeIndexNameMap = DenseMap<unsigned, std::string>; 124 125 // Emits a local variable for each value and attribute to be used for creating 126 // an op. 127 void createSeparateLocalVarsForOpArgs(DagNode node, 128 ChildNodeIndexNameMap &childNodeNames); 129 130 // Emits the concrete arguments used to call an op's builder. 131 void supplyValuesForOpArgs(DagNode node, 132 const ChildNodeIndexNameMap &childNodeNames); 133 134 // Emits the local variables for holding all values as a whole and all named 135 // attributes as a whole to be used for creating an op. 136 void createAggregateLocalVarsForOpArgs( 137 DagNode node, const ChildNodeIndexNameMap &childNodeNames); 138 139 // Returns the C++ expression to construct a constant attribute of the given 140 // `value` for the given attribute kind `attr`. 141 std::string handleConstantAttr(Attribute attr, StringRef value); 142 143 // Returns the C++ expression to build an argument from the given DAG `leaf`. 144 // `patArgName` is used to bound the argument to the source pattern. 145 std::string handleOpArgument(DagLeaf leaf, StringRef patArgName); 146 147 //===--------------------------------------------------------------------===// 148 // General utilities 149 //===--------------------------------------------------------------------===// 150 151 // Collects all of the operations within the given dag tree. 152 void collectOps(DagNode tree, llvm::SmallPtrSetImpl<const Operator *> &ops); 153 154 // Returns a unique symbol for a local variable of the given `op`. 155 std::string getUniqueSymbol(const Operator *op); 156 157 //===--------------------------------------------------------------------===// 158 // Symbol utilities 159 //===--------------------------------------------------------------------===// 160 161 // Returns how many static values the given DAG `node` correspond to. 162 int getNodeValueCount(DagNode node); 163 164 private: 165 // Pattern instantiation location followed by the location of multiclass 166 // prototypes used. This is intended to be used as a whole to 167 // PrintFatalError() on errors. 168 ArrayRef<llvm::SMLoc> loc; 169 170 // Op's TableGen Record to wrapper object. 171 RecordOperatorMap *opMap; 172 173 // Handy wrapper for pattern being emitted. 174 Pattern pattern; 175 176 // Map for all bound symbols' info. 177 SymbolInfoMap symbolInfoMap; 178 179 // The next unused ID for newly created values. 180 unsigned nextValueId; 181 182 raw_ostream &os; 183 184 // Format contexts containing placeholder substitutions. 185 FmtContext fmtCtx; 186 187 // Number of op processed. 188 int opCounter = 0; 189 }; 190 } // end anonymous namespace 191 192 PatternEmitter::PatternEmitter(Record *pat, RecordOperatorMap *mapper, 193 raw_ostream &os) 194 : loc(pat->getLoc()), opMap(mapper), pattern(pat, mapper), 195 symbolInfoMap(pat->getLoc()), nextValueId(0), os(os) { 196 fmtCtx.withBuilder("rewriter"); 197 } 198 199 std::string PatternEmitter::handleConstantAttr(Attribute attr, 200 StringRef value) { 201 if (!attr.isConstBuildable()) 202 PrintFatalError(loc, "Attribute " + attr.getAttrDefName() + 203 " does not have the 'constBuilderCall' field"); 204 205 // TODO(jpienaar): Verify the constants here 206 return std::string(tgfmt(attr.getConstBuilderTemplate(), &fmtCtx, value)); 207 } 208 209 // Helper function to match patterns. 210 void PatternEmitter::emitOpMatch(DagNode tree, int depth) { 211 Operator &op = tree.getDialectOp(opMap); 212 LLVM_DEBUG(llvm::dbgs() << "start emitting match for op '" 213 << op.getOperationName() << "' at depth " << depth 214 << '\n'); 215 216 int indent = 4 + 2 * depth; 217 os.indent(indent) << formatv( 218 "auto castedOp{0} = dyn_cast_or_null<{1}>(op{0}); (void)castedOp{0};\n", 219 depth, op.getQualCppClassName()); 220 // Skip the operand matching at depth 0 as the pattern rewriter already does. 221 if (depth != 0) { 222 // Skip if there is no defining operation (e.g., arguments to function). 223 os.indent(indent) << formatv("if (!castedOp{0}) return failure();\n", 224 depth); 225 } 226 if (tree.getNumArgs() != op.getNumArgs()) { 227 PrintFatalError(loc, formatv("op '{0}' argument number mismatch: {1} in " 228 "pattern vs. {2} in definition", 229 op.getOperationName(), tree.getNumArgs(), 230 op.getNumArgs())); 231 } 232 233 // If the operand's name is set, set to that variable. 234 auto name = tree.getSymbol(); 235 if (!name.empty()) 236 os.indent(indent) << formatv("{0} = castedOp{1};\n", name, depth); 237 238 for (int i = 0, e = tree.getNumArgs(); i != e; ++i) { 239 auto opArg = op.getArg(i); 240 241 // Handle nested DAG construct first 242 if (DagNode argTree = tree.getArgAsNestedDag(i)) { 243 if (auto *operand = opArg.dyn_cast<NamedTypeConstraint *>()) { 244 if (operand->isVariadic()) { 245 auto error = formatv("use nested DAG construct to match op {0}'s " 246 "variadic operand #{1} unsupported now", 247 op.getOperationName(), i); 248 PrintFatalError(loc, error); 249 } 250 } 251 os.indent(indent) << "{\n"; 252 253 os.indent(indent + 2) << formatv( 254 "auto *op{0} = " 255 "(*castedOp{1}.getODSOperands({2}).begin()).getDefiningOp();\n", 256 depth + 1, depth, i); 257 emitOpMatch(argTree, depth + 1); 258 os.indent(indent + 2) 259 << formatv("tblgen_ops[{0}] = op{1};\n", ++opCounter, depth + 1); 260 os.indent(indent) << "}\n"; 261 continue; 262 } 263 264 // Next handle DAG leaf: operand or attribute 265 if (opArg.is<NamedTypeConstraint *>()) { 266 emitOperandMatch(tree, i, depth, indent); 267 } else if (opArg.is<NamedAttribute *>()) { 268 emitAttributeMatch(tree, i, depth, indent); 269 } else { 270 PrintFatalError(loc, "unhandled case when matching op"); 271 } 272 } 273 LLVM_DEBUG(llvm::dbgs() << "done emitting match for op '" 274 << op.getOperationName() << "' at depth " << depth 275 << '\n'); 276 } 277 278 void PatternEmitter::emitOperandMatch(DagNode tree, int argIndex, int depth, 279 int indent) { 280 Operator &op = tree.getDialectOp(opMap); 281 auto *operand = op.getArg(argIndex).get<NamedTypeConstraint *>(); 282 auto matcher = tree.getArgAsLeaf(argIndex); 283 284 // If a constraint is specified, we need to generate C++ statements to 285 // check the constraint. 286 if (!matcher.isUnspecified()) { 287 if (!matcher.isOperandMatcher()) { 288 PrintFatalError( 289 loc, formatv("the {1}-th argument of op '{0}' should be an operand", 290 op.getOperationName(), argIndex + 1)); 291 } 292 293 // Only need to verify if the matcher's type is different from the one 294 // of op definition. 295 Constraint constraint = matcher.getAsConstraint(); 296 if (operand->constraint != constraint) { 297 if (operand->isVariadic()) { 298 auto error = formatv( 299 "further constrain op {0}'s variadic operand #{1} unsupported now", 300 op.getOperationName(), argIndex); 301 PrintFatalError(loc, error); 302 } 303 auto self = 304 formatv("(*castedOp{0}.getODSOperands({1}).begin()).getType()", depth, 305 argIndex); 306 emitMatchCheck( 307 depth, 308 tgfmt(constraint.getConditionTemplate(), &fmtCtx.withSelf(self)), 309 formatv("\"operand {0} of op '{1}' failed to satisfy constraint: " 310 "'{2}'\"", 311 operand - op.operand_begin(), op.getOperationName(), 312 constraint.getDescription())); 313 } 314 } 315 316 // Capture the value 317 auto name = tree.getArgName(argIndex); 318 // `$_` is a special symbol to ignore op argument matching. 319 if (!name.empty() && name != "_") { 320 // We need to subtract the number of attributes before this operand to get 321 // the index in the operand list. 322 auto numPrevAttrs = std::count_if( 323 op.arg_begin(), op.arg_begin() + argIndex, 324 [](const Argument &arg) { return arg.is<NamedAttribute *>(); }); 325 326 os.indent(indent) << formatv("{0} = castedOp{1}.getODSOperands({2});\n", 327 name, depth, argIndex - numPrevAttrs); 328 } 329 } 330 331 void PatternEmitter::emitAttributeMatch(DagNode tree, int argIndex, int depth, 332 int indent) { 333 Operator &op = tree.getDialectOp(opMap); 334 auto *namedAttr = op.getArg(argIndex).get<NamedAttribute *>(); 335 const auto &attr = namedAttr->attr; 336 337 os.indent(indent) << "{\n"; 338 indent += 2; 339 os.indent(indent) << formatv( 340 "auto tblgen_attr = op{0}->getAttrOfType<{1}>(\"{2}\");" 341 "(void)tblgen_attr;\n", 342 depth, attr.getStorageType(), namedAttr->name); 343 344 // TODO(antiagainst): This should use getter method to avoid duplication. 345 if (attr.hasDefaultValue()) { 346 os.indent(indent) << "if (!tblgen_attr) tblgen_attr = " 347 << std::string(tgfmt(attr.getConstBuilderTemplate(), 348 &fmtCtx, attr.getDefaultValue())) 349 << ";\n"; 350 } else if (attr.isOptional()) { 351 // For a missing attribute that is optional according to definition, we 352 // should just capture a mlir::Attribute() to signal the missing state. 353 // That is precisely what getAttr() returns on missing attributes. 354 } else { 355 emitMatchCheck(depth, tgfmt("tblgen_attr", &fmtCtx), 356 formatv("\"expected op '{0}' to have attribute '{1}' " 357 "of type '{2}'\"", 358 op.getOperationName(), namedAttr->name, 359 attr.getStorageType())); 360 } 361 362 auto matcher = tree.getArgAsLeaf(argIndex); 363 if (!matcher.isUnspecified()) { 364 if (!matcher.isAttrMatcher()) { 365 PrintFatalError( 366 loc, formatv("the {1}-th argument of op '{0}' should be an attribute", 367 op.getOperationName(), argIndex + 1)); 368 } 369 370 // If a constraint is specified, we need to generate C++ statements to 371 // check the constraint. 372 emitMatchCheck( 373 depth, 374 tgfmt(matcher.getConditionTemplate(), &fmtCtx.withSelf("tblgen_attr")), 375 formatv("\"op '{0}' attribute '{1}' failed to satisfy constraint: " 376 "{2}\"", 377 op.getOperationName(), namedAttr->name, 378 matcher.getAsConstraint().getDescription())); 379 } 380 381 // Capture the value 382 auto name = tree.getArgName(argIndex); 383 // `$_` is a special symbol to ignore op argument matching. 384 if (!name.empty() && name != "_") { 385 os.indent(indent) << formatv("{0} = tblgen_attr;\n", name); 386 } 387 388 indent -= 2; 389 os.indent(indent) << "}\n"; 390 } 391 392 void PatternEmitter::emitMatchCheck( 393 int depth, const FmtObjectBase &matchFmt, 394 const llvm::formatv_object_base &failureFmt) { 395 // {0} The match depth (used to get the operation that failed to match). 396 // {1} The format for the match string. 397 // {2} The format for the failure string. 398 const char *matchStr = R"( 399 if (!({1})) { 400 return rewriter.notifyMatchFailure(op{0}, [&](::mlir::Diagnostic &diag) { 401 diag << {2}; 402 }); 403 })"; 404 os << llvm::formatv(matchStr, depth, matchFmt.str(), failureFmt.str()) 405 << "\n"; 406 } 407 408 void PatternEmitter::emitMatchLogic(DagNode tree) { 409 LLVM_DEBUG(llvm::dbgs() << "--- start emitting match logic ---\n"); 410 int depth = 0; 411 emitOpMatch(tree, depth); 412 413 for (auto &appliedConstraint : pattern.getConstraints()) { 414 auto &constraint = appliedConstraint.constraint; 415 auto &entities = appliedConstraint.entities; 416 417 auto condition = constraint.getConditionTemplate(); 418 if (isa<TypeConstraint>(constraint)) { 419 auto self = formatv("({0}.getType())", 420 symbolInfoMap.getValueAndRangeUse(entities.front())); 421 emitMatchCheck( 422 depth, tgfmt(condition, &fmtCtx.withSelf(self.str())), 423 formatv("\"value entity '{0}' failed to satisfy constraint: {1}\"", 424 entities.front(), constraint.getDescription())); 425 426 } else if (isa<AttrConstraint>(constraint)) { 427 PrintFatalError( 428 loc, "cannot use AttrConstraint in Pattern multi-entity constraints"); 429 } else { 430 // TODO(b/138794486): replace formatv arguments with the exact specified 431 // args. 432 if (entities.size() > 4) { 433 PrintFatalError(loc, "only support up to 4-entity constraints now"); 434 } 435 SmallVector<std::string, 4> names; 436 int i = 0; 437 for (int e = entities.size(); i < e; ++i) 438 names.push_back(symbolInfoMap.getValueAndRangeUse(entities[i])); 439 std::string self = appliedConstraint.self; 440 if (!self.empty()) 441 self = symbolInfoMap.getValueAndRangeUse(self); 442 for (; i < 4; ++i) 443 names.push_back("<unused>"); 444 emitMatchCheck(depth, 445 tgfmt(condition, &fmtCtx.withSelf(self), names[0], 446 names[1], names[2], names[3]), 447 formatv("\"entities '{0}' failed to satisfy constraint: " 448 "{1}\"", 449 llvm::join(entities, ", "), 450 constraint.getDescription())); 451 } 452 } 453 LLVM_DEBUG(llvm::dbgs() << "--- done emitting match logic ---\n"); 454 } 455 456 void PatternEmitter::collectOps(DagNode tree, 457 llvm::SmallPtrSetImpl<const Operator *> &ops) { 458 // Check if this tree is an operation. 459 if (tree.isOperation()) { 460 const Operator &op = tree.getDialectOp(opMap); 461 LLVM_DEBUG(llvm::dbgs() 462 << "found operation " << op.getOperationName() << '\n'); 463 ops.insert(&op); 464 } 465 466 // Recurse the arguments of the tree. 467 for (unsigned i = 0, e = tree.getNumArgs(); i != e; ++i) 468 if (auto child = tree.getArgAsNestedDag(i)) 469 collectOps(child, ops); 470 } 471 472 void PatternEmitter::emit(StringRef rewriteName) { 473 // Get the DAG tree for the source pattern. 474 DagNode sourceTree = pattern.getSourcePattern(); 475 476 const Operator &rootOp = pattern.getSourceRootOp(); 477 auto rootName = rootOp.getOperationName(); 478 479 // Collect the set of result operations. 480 llvm::SmallPtrSet<const Operator *, 4> resultOps; 481 LLVM_DEBUG(llvm::dbgs() << "start collecting ops used in result patterns\n"); 482 for (unsigned i = 0, e = pattern.getNumResultPatterns(); i != e; ++i) { 483 collectOps(pattern.getResultPattern(i), resultOps); 484 } 485 LLVM_DEBUG(llvm::dbgs() << "done collecting ops used in result patterns\n"); 486 487 // Emit RewritePattern for Pattern. 488 auto locs = pattern.getLocation(); 489 os << formatv("/* Generated from:\n\t{0:$[ instantiating\n\t]}\n*/\n", 490 make_range(locs.rbegin(), locs.rend())); 491 os << formatv(R"(struct {0} : public RewritePattern { 492 {0}(MLIRContext *context) 493 : RewritePattern("{1}", {{)", 494 rewriteName, rootName); 495 // Sort result operators by name. 496 llvm::SmallVector<const Operator *, 4> sortedResultOps(resultOps.begin(), 497 resultOps.end()); 498 llvm::sort(sortedResultOps, [&](const Operator *lhs, const Operator *rhs) { 499 return lhs->getOperationName() < rhs->getOperationName(); 500 }); 501 interleaveComma(sortedResultOps, os, [&](const Operator *op) { 502 os << '"' << op->getOperationName() << '"'; 503 }); 504 os << formatv(R"(}, {0}, context) {{})", pattern.getBenefit()) << "\n"; 505 506 // Emit matchAndRewrite() function. 507 os << R"( 508 LogicalResult matchAndRewrite(Operation *op0, 509 PatternRewriter &rewriter) const override { 510 )"; 511 512 // Register all symbols bound in the source pattern. 513 pattern.collectSourcePatternBoundSymbols(symbolInfoMap); 514 515 LLVM_DEBUG( 516 llvm::dbgs() << "start creating local variables for capturing matches\n"); 517 os.indent(4) << "// Variables for capturing values and attributes used for " 518 "creating ops\n"; 519 // Create local variables for storing the arguments and results bound 520 // to symbols. 521 for (const auto &symbolInfoPair : symbolInfoMap) { 522 StringRef symbol = symbolInfoPair.getKey(); 523 auto &info = symbolInfoPair.getValue(); 524 os.indent(4) << info.getVarDecl(symbol); 525 } 526 // TODO(jpienaar): capture ops with consistent numbering so that it can be 527 // reused for fused loc. 528 os.indent(4) << formatv("Operation *tblgen_ops[{0}];\n\n", 529 pattern.getSourcePattern().getNumOps()); 530 LLVM_DEBUG( 531 llvm::dbgs() << "done creating local variables for capturing matches\n"); 532 533 os.indent(4) << "// Match\n"; 534 os.indent(4) << "tblgen_ops[0] = op0;\n"; 535 emitMatchLogic(sourceTree); 536 os << "\n"; 537 538 os.indent(4) << "// Rewrite\n"; 539 emitRewriteLogic(); 540 541 os.indent(4) << "return success();\n"; 542 os << " };\n"; 543 os << "};\n"; 544 } 545 546 void PatternEmitter::emitRewriteLogic() { 547 LLVM_DEBUG(llvm::dbgs() << "--- start emitting rewrite logic ---\n"); 548 const Operator &rootOp = pattern.getSourceRootOp(); 549 int numExpectedResults = rootOp.getNumResults(); 550 int numResultPatterns = pattern.getNumResultPatterns(); 551 552 // First register all symbols bound to ops generated in result patterns. 553 pattern.collectResultPatternBoundSymbols(symbolInfoMap); 554 555 // Only the last N static values generated are used to replace the matched 556 // root N-result op. We need to calculate the starting index (of the results 557 // of the matched op) each result pattern is to replace. 558 SmallVector<int, 4> offsets(numResultPatterns + 1, numExpectedResults); 559 // If we don't need to replace any value at all, set the replacement starting 560 // index as the number of result patterns so we skip all of them when trying 561 // to replace the matched op's results. 562 int replStartIndex = numExpectedResults == 0 ? numResultPatterns : -1; 563 for (int i = numResultPatterns - 1; i >= 0; --i) { 564 auto numValues = getNodeValueCount(pattern.getResultPattern(i)); 565 offsets[i] = offsets[i + 1] - numValues; 566 if (offsets[i] == 0) { 567 if (replStartIndex == -1) 568 replStartIndex = i; 569 } else if (offsets[i] < 0 && offsets[i + 1] > 0) { 570 auto error = formatv( 571 "cannot use the same multi-result op '{0}' to generate both " 572 "auxiliary values and values to be used for replacing the matched op", 573 pattern.getResultPattern(i).getSymbol()); 574 PrintFatalError(loc, error); 575 } 576 } 577 578 if (offsets.front() > 0) { 579 const char error[] = "no enough values generated to replace the matched op"; 580 PrintFatalError(loc, error); 581 } 582 583 os.indent(4) << "auto loc = rewriter.getFusedLoc({"; 584 for (int i = 0, e = pattern.getSourcePattern().getNumOps(); i != e; ++i) { 585 os << (i ? ", " : "") << "tblgen_ops[" << i << "]->getLoc()"; 586 } 587 os << "}); (void)loc;\n"; 588 589 // Process auxiliary result patterns. 590 for (int i = 0; i < replStartIndex; ++i) { 591 DagNode resultTree = pattern.getResultPattern(i); 592 auto val = handleResultPattern(resultTree, offsets[i], 0); 593 // Normal op creation will be streamed to `os` by the above call; but 594 // NativeCodeCall will only be materialized to `os` if it is used. Here 595 // we are handling auxiliary patterns so we want the side effect even if 596 // NativeCodeCall is not replacing matched root op's results. 597 if (resultTree.isNativeCodeCall()) 598 os.indent(4) << val << ";\n"; 599 } 600 601 if (numExpectedResults == 0) { 602 assert(replStartIndex >= numResultPatterns && 603 "invalid auxiliary vs. replacement pattern division!"); 604 // No result to replace. Just erase the op. 605 os.indent(4) << "rewriter.eraseOp(op0);\n"; 606 } else { 607 // Process replacement result patterns. 608 os.indent(4) << "SmallVector<Value, 4> tblgen_repl_values;\n"; 609 for (int i = replStartIndex; i < numResultPatterns; ++i) { 610 DagNode resultTree = pattern.getResultPattern(i); 611 auto val = handleResultPattern(resultTree, offsets[i], 0); 612 os.indent(4) << "\n"; 613 // Resolve each symbol for all range use so that we can loop over them. 614 // We need an explicit cast to `SmallVector` to capture the cases where 615 // `{0}` resolves to an `Operation::result_range` as well as cases that 616 // are not iterable (e.g. vector that gets wrapped in additional braces by 617 // RewriterGen). 618 // TODO(b/147096809): Revisit the need for materializing a vector. 619 os << symbolInfoMap.getAllRangeUse( 620 val, 621 " for (auto v : SmallVector<Value, 4>{ {0} }) {{ " 622 "tblgen_repl_values.push_back(v); }", 623 "\n"); 624 } 625 os.indent(4) << "\n"; 626 os.indent(4) << "rewriter.replaceOp(op0, tblgen_repl_values);\n"; 627 } 628 629 LLVM_DEBUG(llvm::dbgs() << "--- done emitting rewrite logic ---\n"); 630 } 631 632 std::string PatternEmitter::getUniqueSymbol(const Operator *op) { 633 return std::string( 634 formatv("tblgen_{0}_{1}", op->getCppClassName(), nextValueId++)); 635 } 636 637 std::string PatternEmitter::handleResultPattern(DagNode resultTree, 638 int resultIndex, int depth) { 639 LLVM_DEBUG(llvm::dbgs() << "handle result pattern: "); 640 LLVM_DEBUG(resultTree.print(llvm::dbgs())); 641 LLVM_DEBUG(llvm::dbgs() << '\n'); 642 643 if (resultTree.isNativeCodeCall()) { 644 auto symbol = handleReplaceWithNativeCodeCall(resultTree); 645 symbolInfoMap.bindValue(symbol); 646 return symbol; 647 } 648 649 if (resultTree.isReplaceWithValue()) { 650 return handleReplaceWithValue(resultTree); 651 } 652 653 // Normal op creation. 654 auto symbol = handleOpCreation(resultTree, resultIndex, depth); 655 if (resultTree.getSymbol().empty()) { 656 // This is an op not explicitly bound to a symbol in the rewrite rule. 657 // Register the auto-generated symbol for it. 658 symbolInfoMap.bindOpResult(symbol, pattern.getDialectOp(resultTree)); 659 } 660 return symbol; 661 } 662 663 std::string PatternEmitter::handleReplaceWithValue(DagNode tree) { 664 assert(tree.isReplaceWithValue()); 665 666 if (tree.getNumArgs() != 1) { 667 PrintFatalError( 668 loc, "replaceWithValue directive must take exactly one argument"); 669 } 670 671 if (!tree.getSymbol().empty()) { 672 PrintFatalError(loc, "cannot bind symbol to replaceWithValue"); 673 } 674 675 return std::string(tree.getArgName(0)); 676 } 677 678 std::string PatternEmitter::handleOpArgument(DagLeaf leaf, 679 StringRef patArgName) { 680 if (leaf.isConstantAttr()) { 681 auto constAttr = leaf.getAsConstantAttr(); 682 return handleConstantAttr(constAttr.getAttribute(), 683 constAttr.getConstantValue()); 684 } 685 if (leaf.isEnumAttrCase()) { 686 auto enumCase = leaf.getAsEnumAttrCase(); 687 if (enumCase.isStrCase()) 688 return handleConstantAttr(enumCase, enumCase.getSymbol()); 689 // This is an enum case backed by an IntegerAttr. We need to get its value 690 // to build the constant. 691 std::string val = std::to_string(enumCase.getValue()); 692 return handleConstantAttr(enumCase, val); 693 } 694 695 LLVM_DEBUG(llvm::dbgs() << "handle argument '" << patArgName << "'\n"); 696 auto argName = symbolInfoMap.getValueAndRangeUse(patArgName); 697 if (leaf.isUnspecified() || leaf.isOperandMatcher()) { 698 LLVM_DEBUG(llvm::dbgs() << "replace " << patArgName << " with '" << argName 699 << "' (via symbol ref)\n"); 700 return argName; 701 } 702 if (leaf.isNativeCodeCall()) { 703 auto repl = tgfmt(leaf.getNativeCodeTemplate(), &fmtCtx.withSelf(argName)); 704 LLVM_DEBUG(llvm::dbgs() << "replace " << patArgName << " with '" << repl 705 << "' (via NativeCodeCall)\n"); 706 return std::string(repl); 707 } 708 PrintFatalError(loc, "unhandled case when rewriting op"); 709 } 710 711 std::string PatternEmitter::handleReplaceWithNativeCodeCall(DagNode tree) { 712 LLVM_DEBUG(llvm::dbgs() << "handle NativeCodeCall pattern: "); 713 LLVM_DEBUG(tree.print(llvm::dbgs())); 714 LLVM_DEBUG(llvm::dbgs() << '\n'); 715 716 auto fmt = tree.getNativeCodeTemplate(); 717 // TODO(b/138794486): replace formatv arguments with the exact specified args. 718 SmallVector<std::string, 8> attrs(8); 719 if (tree.getNumArgs() > 8) { 720 PrintFatalError(loc, "unsupported NativeCodeCall argument numbers: " + 721 Twine(tree.getNumArgs())); 722 } 723 for (int i = 0, e = tree.getNumArgs(); i != e; ++i) { 724 attrs[i] = handleOpArgument(tree.getArgAsLeaf(i), tree.getArgName(i)); 725 LLVM_DEBUG(llvm::dbgs() << "NativeCodeCall argument #" << i 726 << " replacement: " << attrs[i] << "\n"); 727 } 728 return std::string(tgfmt(fmt, &fmtCtx, attrs[0], attrs[1], attrs[2], attrs[3], 729 attrs[4], attrs[5], attrs[6], attrs[7])); 730 } 731 732 int PatternEmitter::getNodeValueCount(DagNode node) { 733 if (node.isOperation()) { 734 // If the op is bound to a symbol in the rewrite rule, query its result 735 // count from the symbol info map. 736 auto symbol = node.getSymbol(); 737 if (!symbol.empty()) { 738 return symbolInfoMap.getStaticValueCount(symbol); 739 } 740 // Otherwise this is an unbound op; we will use all its results. 741 return pattern.getDialectOp(node).getNumResults(); 742 } 743 // TODO(antiagainst): This considers all NativeCodeCall as returning one 744 // value. Enhance if multi-value ones are needed. 745 return 1; 746 } 747 748 std::string PatternEmitter::handleOpCreation(DagNode tree, int resultIndex, 749 int depth) { 750 LLVM_DEBUG(llvm::dbgs() << "create op for pattern: "); 751 LLVM_DEBUG(tree.print(llvm::dbgs())); 752 LLVM_DEBUG(llvm::dbgs() << '\n'); 753 754 Operator &resultOp = tree.getDialectOp(opMap); 755 auto numOpArgs = resultOp.getNumArgs(); 756 757 if (numOpArgs != tree.getNumArgs()) { 758 PrintFatalError(loc, formatv("resultant op '{0}' argument number mismatch: " 759 "{1} in pattern vs. {2} in definition", 760 resultOp.getOperationName(), tree.getNumArgs(), 761 numOpArgs)); 762 } 763 764 // A map to collect all nested DAG child nodes' names, with operand index as 765 // the key. This includes both bound and unbound child nodes. 766 ChildNodeIndexNameMap childNodeNames; 767 768 // First go through all the child nodes who are nested DAG constructs to 769 // create ops for them and remember the symbol names for them, so that we can 770 // use the results in the current node. This happens in a recursive manner. 771 for (int i = 0, e = resultOp.getNumOperands(); i != e; ++i) { 772 if (auto child = tree.getArgAsNestedDag(i)) { 773 childNodeNames[i] = handleResultPattern(child, i, depth + 1); 774 } 775 } 776 777 // The name of the local variable holding this op. 778 std::string valuePackName; 779 // The symbol for holding the result of this pattern. Note that the result of 780 // this pattern is not necessarily the same as the variable created by this 781 // pattern because we can use `__N` suffix to refer only a specific result if 782 // the generated op is a multi-result op. 783 std::string resultValue; 784 if (tree.getSymbol().empty()) { 785 // No symbol is explicitly bound to this op in the pattern. Generate a 786 // unique name. 787 valuePackName = resultValue = getUniqueSymbol(&resultOp); 788 } else { 789 resultValue = std::string(tree.getSymbol()); 790 // Strip the index to get the name for the value pack and use it to name the 791 // local variable for the op. 792 valuePackName = std::string(SymbolInfoMap::getValuePackName(resultValue)); 793 } 794 795 // Create the local variable for this op. 796 os.indent(4) << formatv("{0} {1};\n", resultOp.getQualCppClassName(), 797 valuePackName); 798 os.indent(4) << "{\n"; 799 800 // Right now ODS don't have general type inference support. Except a few 801 // special cases listed below, DRR needs to supply types for all results 802 // when building an op. 803 bool isSameOperandsAndResultType = 804 resultOp.getTrait("OpTrait::SameOperandsAndResultType"); 805 bool useFirstAttr = resultOp.getTrait("OpTrait::FirstAttrDerivedResultType"); 806 807 if (isSameOperandsAndResultType || useFirstAttr) { 808 // We know how to deduce the result type for ops with these traits and we've 809 // generated builders taking aggregate parameters. Use those builders to 810 // create the ops. 811 812 // First prepare local variables for op arguments used in builder call. 813 createAggregateLocalVarsForOpArgs(tree, childNodeNames); 814 // Then create the op. 815 os.indent(6) << formatv( 816 "{0} = rewriter.create<{1}>(loc, tblgen_values, tblgen_attrs);\n", 817 valuePackName, resultOp.getQualCppClassName()); 818 os.indent(4) << "}\n"; 819 return resultValue; 820 } 821 822 // TODO: Remove once broadcastable has been updated. This query here is not 823 // really about broadcastable or not, it is about which build method to invoke 824 // and that requires knowledge of whether ODS generated a builder that need 825 // not take return types. That knowledge should be captured in one place 826 // rather than duplicated. 827 bool isResultsBroadcastableShape = 828 resultOp.getTrait("OpTrait::ResultsBroadcastableShape"); 829 bool usePartialResults = valuePackName != resultValue; 830 831 if (isResultsBroadcastableShape || usePartialResults || depth > 0 || 832 resultIndex < 0) { 833 // For these cases (broadcastable ops, op results used both as auxiliary 834 // values and replacement values, ops in nested patterns, auxiliary ops), we 835 // still need to supply the result types when building the op. But because 836 // we don't generate a builder automatically with ODS for them, it's the 837 // developer's responsibility to make sure such a builder (with result type 838 // deduction ability) exists. We go through the separate-parameter builder 839 // here given that it's easier for developers to write compared to 840 // aggregate-parameter builders. 841 createSeparateLocalVarsForOpArgs(tree, childNodeNames); 842 os.indent(6) << formatv("{0} = rewriter.create<{1}>(loc", valuePackName, 843 resultOp.getQualCppClassName()); 844 supplyValuesForOpArgs(tree, childNodeNames); 845 os << "\n );\n"; 846 os.indent(4) << "}\n"; 847 return resultValue; 848 } 849 850 // If depth == 0 and resultIndex >= 0, it means we are replacing the values 851 // generated from the source pattern root op. Then we can use the source 852 // pattern's value types to determine the value type of the generated op 853 // here. 854 855 // First prepare local variables for op arguments used in builder call. 856 createAggregateLocalVarsForOpArgs(tree, childNodeNames); 857 858 // Then prepare the result types. We need to specify the types for all 859 // results. 860 os.indent(6) << formatv( 861 "SmallVector<Type, 4> tblgen_types; (void)tblgen_types;\n"); 862 int numResults = resultOp.getNumResults(); 863 if (numResults != 0) { 864 for (int i = 0; i < numResults; ++i) 865 os.indent(6) << formatv("for (auto v : castedOp0.getODSResults({0})) {{" 866 "tblgen_types.push_back(v.getType()); }\n", 867 resultIndex + i); 868 } 869 os.indent(6) << formatv("{0} = rewriter.create<{1}>(loc, tblgen_types, " 870 "tblgen_values, tblgen_attrs);\n", 871 valuePackName, resultOp.getQualCppClassName()); 872 os.indent(4) << "}\n"; 873 return resultValue; 874 } 875 876 void PatternEmitter::createSeparateLocalVarsForOpArgs( 877 DagNode node, ChildNodeIndexNameMap &childNodeNames) { 878 Operator &resultOp = node.getDialectOp(opMap); 879 880 // Now prepare operands used for building this op: 881 // * If the operand is non-variadic, we create a `Value` local variable. 882 // * If the operand is variadic, we create a `SmallVector<Value>` local 883 // variable. 884 885 int valueIndex = 0; // An index for uniquing local variable names. 886 for (int argIndex = 0, e = resultOp.getNumArgs(); argIndex < e; ++argIndex) { 887 const auto *operand = 888 resultOp.getArg(argIndex).dyn_cast<NamedTypeConstraint *>(); 889 if (!operand) { 890 // We do not need special handling for attributes. 891 continue; 892 } 893 894 std::string varName; 895 if (operand->isVariadic()) { 896 varName = std::string(formatv("tblgen_values_{0}", valueIndex++)); 897 os.indent(6) << formatv("SmallVector<Value, 4> {0};\n", varName); 898 std::string range; 899 if (node.isNestedDagArg(argIndex)) { 900 range = childNodeNames[argIndex]; 901 } else { 902 range = std::string(node.getArgName(argIndex)); 903 } 904 // Resolve the symbol for all range use so that we have a uniform way of 905 // capturing the values. 906 range = symbolInfoMap.getValueAndRangeUse(range); 907 os.indent(6) << formatv("for (auto v : {0}) {1}.push_back(v);\n", range, 908 varName); 909 } else { 910 varName = std::string(formatv("tblgen_value_{0}", valueIndex++)); 911 os.indent(6) << formatv("Value {0} = ", varName); 912 if (node.isNestedDagArg(argIndex)) { 913 os << symbolInfoMap.getValueAndRangeUse(childNodeNames[argIndex]); 914 } else { 915 DagLeaf leaf = node.getArgAsLeaf(argIndex); 916 auto symbol = 917 symbolInfoMap.getValueAndRangeUse(node.getArgName(argIndex)); 918 if (leaf.isNativeCodeCall()) { 919 os << std::string( 920 tgfmt(leaf.getNativeCodeTemplate(), &fmtCtx.withSelf(symbol))); 921 } else { 922 os << symbol; 923 } 924 } 925 os << ";\n"; 926 } 927 928 // Update to use the newly created local variable for building the op later. 929 childNodeNames[argIndex] = varName; 930 } 931 } 932 933 void PatternEmitter::supplyValuesForOpArgs( 934 DagNode node, const ChildNodeIndexNameMap &childNodeNames) { 935 Operator &resultOp = node.getDialectOp(opMap); 936 for (int argIndex = 0, numOpArgs = resultOp.getNumArgs(); 937 argIndex != numOpArgs; ++argIndex) { 938 // Start each argument on its own line. 939 (os << ",\n").indent(8); 940 941 Argument opArg = resultOp.getArg(argIndex); 942 // Handle the case of operand first. 943 if (auto *operand = opArg.dyn_cast<NamedTypeConstraint *>()) { 944 if (!operand->name.empty()) 945 os << "/*" << operand->name << "=*/"; 946 os << childNodeNames.lookup(argIndex); 947 continue; 948 } 949 950 // The argument in the op definition. 951 auto opArgName = resultOp.getArgName(argIndex); 952 if (auto subTree = node.getArgAsNestedDag(argIndex)) { 953 if (!subTree.isNativeCodeCall()) 954 PrintFatalError(loc, "only NativeCodeCall allowed in nested dag node " 955 "for creating attribute"); 956 os << formatv("/*{0}=*/{1}", opArgName, 957 handleReplaceWithNativeCodeCall(subTree)); 958 } else { 959 auto leaf = node.getArgAsLeaf(argIndex); 960 // The argument in the result DAG pattern. 961 auto patArgName = node.getArgName(argIndex); 962 if (leaf.isConstantAttr() || leaf.isEnumAttrCase()) { 963 // TODO(jpienaar): Refactor out into map to avoid recomputing these. 964 if (!opArg.is<NamedAttribute *>()) 965 PrintFatalError(loc, Twine("expected attribute ") + Twine(argIndex)); 966 if (!patArgName.empty()) 967 os << "/*" << patArgName << "=*/"; 968 } else { 969 os << "/*" << opArgName << "=*/"; 970 } 971 os << handleOpArgument(leaf, patArgName); 972 } 973 } 974 } 975 976 void PatternEmitter::createAggregateLocalVarsForOpArgs( 977 DagNode node, const ChildNodeIndexNameMap &childNodeNames) { 978 Operator &resultOp = node.getDialectOp(opMap); 979 980 os.indent(6) << formatv( 981 "SmallVector<Value, 4> tblgen_values; (void)tblgen_values;\n"); 982 os.indent(6) << formatv( 983 "SmallVector<NamedAttribute, 4> tblgen_attrs; (void)tblgen_attrs;\n"); 984 985 for (int argIndex = 0, e = resultOp.getNumArgs(); argIndex < e; ++argIndex) { 986 if (resultOp.getArg(argIndex).is<NamedAttribute *>()) { 987 const char *addAttrCmd = "if ({1}) {{" 988 " tblgen_attrs.emplace_back(rewriter." 989 "getIdentifier(\"{0}\"), {1}); }\n"; 990 // The argument in the op definition. 991 auto opArgName = resultOp.getArgName(argIndex); 992 if (auto subTree = node.getArgAsNestedDag(argIndex)) { 993 if (!subTree.isNativeCodeCall()) 994 PrintFatalError(loc, "only NativeCodeCall allowed in nested dag node " 995 "for creating attribute"); 996 os.indent(6) << formatv(addAttrCmd, opArgName, 997 handleReplaceWithNativeCodeCall(subTree)); 998 } else { 999 auto leaf = node.getArgAsLeaf(argIndex); 1000 // The argument in the result DAG pattern. 1001 auto patArgName = node.getArgName(argIndex); 1002 os.indent(6) << formatv(addAttrCmd, opArgName, 1003 handleOpArgument(leaf, patArgName)); 1004 } 1005 continue; 1006 } 1007 1008 const auto *operand = 1009 resultOp.getArg(argIndex).get<NamedTypeConstraint *>(); 1010 std::string varName; 1011 if (operand->isVariadic()) { 1012 std::string range; 1013 if (node.isNestedDagArg(argIndex)) { 1014 range = childNodeNames.lookup(argIndex); 1015 } else { 1016 range = std::string(node.getArgName(argIndex)); 1017 } 1018 // Resolve the symbol for all range use so that we have a uniform way of 1019 // capturing the values. 1020 range = symbolInfoMap.getValueAndRangeUse(range); 1021 os.indent(6) << formatv( 1022 "for (auto v : {0}) tblgen_values.push_back(v);\n", range); 1023 } else { 1024 os.indent(6) << formatv("tblgen_values.push_back(", varName); 1025 if (node.isNestedDagArg(argIndex)) { 1026 os << symbolInfoMap.getValueAndRangeUse( 1027 childNodeNames.lookup(argIndex)); 1028 } else { 1029 DagLeaf leaf = node.getArgAsLeaf(argIndex); 1030 auto symbol = 1031 symbolInfoMap.getValueAndRangeUse(node.getArgName(argIndex)); 1032 if (leaf.isNativeCodeCall()) { 1033 os << std::string( 1034 tgfmt(leaf.getNativeCodeTemplate(), &fmtCtx.withSelf(symbol))); 1035 } else { 1036 os << symbol; 1037 } 1038 } 1039 os << ");\n"; 1040 } 1041 } 1042 } 1043 1044 static void emitRewriters(const RecordKeeper &recordKeeper, raw_ostream &os) { 1045 emitSourceFileHeader("Rewriters", os); 1046 1047 const auto &patterns = recordKeeper.getAllDerivedDefinitions("Pattern"); 1048 auto numPatterns = patterns.size(); 1049 1050 // We put the map here because it can be shared among multiple patterns. 1051 RecordOperatorMap recordOpMap; 1052 1053 std::vector<std::string> rewriterNames; 1054 rewriterNames.reserve(numPatterns); 1055 1056 std::string baseRewriterName = "GeneratedConvert"; 1057 int rewriterIndex = 0; 1058 1059 for (Record *p : patterns) { 1060 std::string name; 1061 if (p->isAnonymous()) { 1062 // If no name is provided, ensure unique rewriter names simply by 1063 // appending unique suffix. 1064 name = baseRewriterName + llvm::utostr(rewriterIndex++); 1065 } else { 1066 name = std::string(p->getName()); 1067 } 1068 LLVM_DEBUG(llvm::dbgs() 1069 << "=== start generating pattern '" << name << "' ===\n"); 1070 PatternEmitter(p, &recordOpMap, os).emit(name); 1071 LLVM_DEBUG(llvm::dbgs() 1072 << "=== done generating pattern '" << name << "' ===\n"); 1073 rewriterNames.push_back(std::move(name)); 1074 } 1075 1076 // Emit function to add the generated matchers to the pattern list. 1077 os << "void LLVM_ATTRIBUTE_UNUSED populateWithGenerated(MLIRContext " 1078 "*context, OwningRewritePatternList *patterns) {\n"; 1079 for (const auto &name : rewriterNames) { 1080 os << " patterns->insert<" << name << ">(context);\n"; 1081 } 1082 os << "}\n"; 1083 } 1084 1085 static mlir::GenRegistration 1086 genRewriters("gen-rewriters", "Generate pattern rewriters", 1087 [](const RecordKeeper &records, raw_ostream &os) { 1088 emitRewriters(records, os); 1089 return false; 1090 }); 1091