1 //===- PDL.cpp - Pattern Descriptor Language Dialect ----------------------===//
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 #include "mlir/Dialect/PDL/IR/PDL.h"
10 #include "mlir/Dialect/PDL/IR/PDLOps.h"
11 #include "mlir/Dialect/PDL/IR/PDLTypes.h"
12 #include "mlir/IR/BuiltinTypes.h"
13 #include "mlir/Interfaces/InferTypeOpInterface.h"
14 #include "llvm/ADT/DenseSet.h"
15 #include "llvm/ADT/TypeSwitch.h"
16 
17 using namespace mlir;
18 using namespace mlir::pdl;
19 
20 #include "mlir/Dialect/PDL/IR/PDLOpsDialect.cpp.inc"
21 
22 //===----------------------------------------------------------------------===//
23 // PDLDialect
24 //===----------------------------------------------------------------------===//
25 
26 void PDLDialect::initialize() {
27   addOperations<
28 #define GET_OP_LIST
29 #include "mlir/Dialect/PDL/IR/PDLOps.cpp.inc"
30       >();
31   registerTypes();
32 }
33 
34 //===----------------------------------------------------------------------===//
35 // PDL Operations
36 //===----------------------------------------------------------------------===//
37 
38 /// Returns true if the given operation is used by a "binding" pdl operation.
39 static bool hasBindingUse(Operation *op) {
40   for (Operation *user : op->getUsers())
41     // A result by itself is not binding, it must also be bound.
42     if (!isa<ResultOp, ResultsOp>(user) || hasBindingUse(user))
43       return true;
44   return false;
45 }
46 
47 /// Returns success if the given operation is not in the main matcher body or
48 /// is used by a "binding" operation. On failure, emits an error.
49 static LogicalResult verifyHasBindingUse(Operation *op) {
50   // If the parent is not a pattern, there is nothing to do.
51   if (!isa<PatternOp>(op->getParentOp()))
52     return success();
53   if (hasBindingUse(op))
54     return success();
55   return op->emitOpError(
56       "expected a bindable user when defined in the matcher body of a "
57       "`pdl.pattern`");
58 }
59 
60 /// Visits all the pdl.operand(s), pdl.result(s), and pdl.operation(s)
61 /// connected to the given operation.
62 static void visit(Operation *op, DenseSet<Operation *> &visited) {
63   // If the parent is not a pattern, there is nothing to do.
64   if (!isa<PatternOp>(op->getParentOp()) || isa<RewriteOp>(op))
65     return;
66 
67   // Ignore if already visited.
68   if (visited.contains(op))
69     return;
70 
71   // Mark as visited.
72   visited.insert(op);
73 
74   // Traverse the operands / parent.
75   TypeSwitch<Operation *>(op)
76       .Case<OperationOp>([&visited](auto operation) {
77         for (Value operand : operation.operands())
78           visit(operand.getDefiningOp(), visited);
79       })
80       .Case<ResultOp, ResultsOp>([&visited](auto result) {
81         visit(result.parent().getDefiningOp(), visited);
82       });
83 
84   // Traverse the users.
85   for (Operation *user : op->getUsers())
86     visit(user, visited);
87 }
88 
89 //===----------------------------------------------------------------------===//
90 // pdl::ApplyNativeConstraintOp
91 //===----------------------------------------------------------------------===//
92 
93 LogicalResult ApplyNativeConstraintOp::verify() {
94   if (getNumOperands() == 0)
95     return emitOpError("expected at least one argument");
96   return success();
97 }
98 
99 //===----------------------------------------------------------------------===//
100 // pdl::ApplyNativeRewriteOp
101 //===----------------------------------------------------------------------===//
102 
103 LogicalResult ApplyNativeRewriteOp::verify() {
104   if (getNumOperands() == 0 && getNumResults() == 0)
105     return emitOpError("expected at least one argument or result");
106   return success();
107 }
108 
109 //===----------------------------------------------------------------------===//
110 // pdl::AttributeOp
111 //===----------------------------------------------------------------------===//
112 
113 LogicalResult AttributeOp::verify() {
114   Value attrType = type();
115   Optional<Attribute> attrValue = value();
116 
117   if (!attrValue) {
118     if (isa<RewriteOp>((*this)->getParentOp()))
119       return emitOpError(
120           "expected constant value when specified within a `pdl.rewrite`");
121     return verifyHasBindingUse(*this);
122   }
123   if (attrType)
124     return emitOpError("expected only one of [`type`, `value`] to be set");
125   return success();
126 }
127 
128 //===----------------------------------------------------------------------===//
129 // pdl::OperandOp
130 //===----------------------------------------------------------------------===//
131 
132 LogicalResult OperandOp::verify() { return verifyHasBindingUse(*this); }
133 
134 //===----------------------------------------------------------------------===//
135 // pdl::OperandsOp
136 //===----------------------------------------------------------------------===//
137 
138 LogicalResult OperandsOp::verify() { return verifyHasBindingUse(*this); }
139 
140 //===----------------------------------------------------------------------===//
141 // pdl::OperationOp
142 //===----------------------------------------------------------------------===//
143 
144 static ParseResult parseOperationOpAttributes(
145     OpAsmParser &p,
146     SmallVectorImpl<OpAsmParser::UnresolvedOperand> &attrOperands,
147     ArrayAttr &attrNamesAttr) {
148   Builder &builder = p.getBuilder();
149   SmallVector<Attribute, 4> attrNames;
150   if (succeeded(p.parseOptionalLBrace())) {
151     do {
152       StringAttr nameAttr;
153       OpAsmParser::UnresolvedOperand operand;
154       if (p.parseAttribute(nameAttr) || p.parseEqual() ||
155           p.parseOperand(operand))
156         return failure();
157       attrNames.push_back(nameAttr);
158       attrOperands.push_back(operand);
159     } while (succeeded(p.parseOptionalComma()));
160     if (p.parseRBrace())
161       return failure();
162   }
163   attrNamesAttr = builder.getArrayAttr(attrNames);
164   return success();
165 }
166 
167 static void printOperationOpAttributes(OpAsmPrinter &p, OperationOp op,
168                                        OperandRange attrArgs,
169                                        ArrayAttr attrNames) {
170   if (attrNames.empty())
171     return;
172   p << " {";
173   interleaveComma(llvm::seq<int>(0, attrNames.size()), p,
174                   [&](int i) { p << attrNames[i] << " = " << attrArgs[i]; });
175   p << '}';
176 }
177 
178 /// Verifies that the result types of this operation, defined within a
179 /// `pdl.rewrite`, can be inferred.
180 static LogicalResult verifyResultTypesAreInferrable(OperationOp op,
181                                                     OperandRange resultTypes) {
182   // Functor that returns if the given use can be used to infer a type.
183   Block *rewriterBlock = op->getBlock();
184   auto canInferTypeFromUse = [&](OpOperand &use) {
185     // If the use is within a ReplaceOp and isn't the operation being replaced
186     // (i.e. is not the first operand of the replacement), we can infer a type.
187     ReplaceOp replOpUser = dyn_cast<ReplaceOp>(use.getOwner());
188     if (!replOpUser || use.getOperandNumber() == 0)
189       return false;
190     // Make sure the replaced operation was defined before this one.
191     Operation *replacedOp = replOpUser.operation().getDefiningOp();
192     return replacedOp->getBlock() != rewriterBlock ||
193            replacedOp->isBeforeInBlock(op);
194   };
195 
196   // Check to see if the uses of the operation itself can be used to infer
197   // types.
198   if (llvm::any_of(op.op().getUses(), canInferTypeFromUse))
199     return success();
200 
201   // Handle the case where the operation has no explicit result types.
202   if (resultTypes.empty()) {
203     // If we don't know the concrete operation, don't attempt any verification.
204     // We can't make assumptions if we don't know the concrete operation.
205     Optional<StringRef> rawOpName = op.name();
206     if (!rawOpName)
207       return success();
208     Optional<RegisteredOperationName> opName =
209         RegisteredOperationName::lookup(*rawOpName, op.getContext());
210     if (!opName)
211       return success();
212 
213     // If no explicit result types were provided, check to see if the operation
214     // expected at least one result. This doesn't cover all cases, but this
215     // should cover many cases in which the user intended to infer the results
216     // of an operation, but it isn't actually possible.
217     bool expectedAtLeastOneResult =
218         !opName->hasTrait<OpTrait::ZeroResult>() &&
219         !opName->hasTrait<OpTrait::VariadicResults>();
220     if (expectedAtLeastOneResult) {
221       return op
222           .emitOpError("must have inferable or constrained result types when "
223                        "nested within `pdl.rewrite`")
224           .attachNote()
225           .append("operation is created in a non-inferrable context, but '",
226                   *opName, "' does not implement InferTypeOpInterface");
227     }
228     return success();
229   }
230 
231   // Otherwise, make sure each of the types can be inferred.
232   for (const auto &it : llvm::enumerate(resultTypes)) {
233     Operation *resultTypeOp = it.value().getDefiningOp();
234     assert(resultTypeOp && "expected valid result type operation");
235 
236     // If the op was defined by a `apply_native_rewrite`, it is guaranteed to be
237     // usable.
238     if (isa<ApplyNativeRewriteOp>(resultTypeOp))
239       continue;
240 
241     // If the type operation was defined in the matcher and constrains an
242     // operand or the result of an input operation, it can be used.
243     auto constrainsInput = [rewriterBlock](Operation *user) {
244       return user->getBlock() != rewriterBlock &&
245              isa<OperandOp, OperandsOp, OperationOp>(user);
246     };
247     if (TypeOp typeOp = dyn_cast<TypeOp>(resultTypeOp)) {
248       if (typeOp.type() || llvm::any_of(typeOp->getUsers(), constrainsInput))
249         continue;
250     } else if (TypesOp typeOp = dyn_cast<TypesOp>(resultTypeOp)) {
251       if (typeOp.types() || llvm::any_of(typeOp->getUsers(), constrainsInput))
252         continue;
253     }
254 
255     return op
256         .emitOpError("must have inferable or constrained result types when "
257                      "nested within `pdl.rewrite`")
258         .attachNote()
259         .append("result type #", it.index(), " was not constrained");
260   }
261   return success();
262 }
263 
264 LogicalResult OperationOp::verify() {
265   bool isWithinRewrite = isa<RewriteOp>((*this)->getParentOp());
266   if (isWithinRewrite && !name())
267     return emitOpError("must have an operation name when nested within "
268                        "a `pdl.rewrite`");
269   ArrayAttr attributeNames = attributeNamesAttr();
270   auto attributeValues = attributes();
271   if (attributeNames.size() != attributeValues.size()) {
272     return emitOpError()
273            << "expected the same number of attribute values and attribute "
274               "names, got "
275            << attributeNames.size() << " names and " << attributeValues.size()
276            << " values";
277   }
278 
279   // If the operation is within a rewrite body and doesn't have type inference,
280   // ensure that the result types can be resolved.
281   if (isWithinRewrite && !mightHaveTypeInference()) {
282     if (failed(verifyResultTypesAreInferrable(*this, types())))
283       return failure();
284   }
285 
286   return verifyHasBindingUse(*this);
287 }
288 
289 bool OperationOp::hasTypeInference() {
290   if (Optional<StringRef> rawOpName = name()) {
291     OperationName opName(*rawOpName, getContext());
292     return opName.hasInterface<InferTypeOpInterface>();
293   }
294   return false;
295 }
296 
297 bool OperationOp::mightHaveTypeInference() {
298   if (Optional<StringRef> rawOpName = name()) {
299     OperationName opName(*rawOpName, getContext());
300     return opName.mightHaveInterface<InferTypeOpInterface>();
301   }
302   return false;
303 }
304 
305 //===----------------------------------------------------------------------===//
306 // pdl::PatternOp
307 //===----------------------------------------------------------------------===//
308 
309 LogicalResult PatternOp::verifyRegions() {
310   Region &body = getBodyRegion();
311   Operation *term = body.front().getTerminator();
312   auto rewriteOp = dyn_cast<RewriteOp>(term);
313   if (!rewriteOp) {
314     return emitOpError("expected body to terminate with `pdl.rewrite`")
315         .attachNote(term->getLoc())
316         .append("see terminator defined here");
317   }
318 
319   // Check that all values defined in the top-level pattern belong to the PDL
320   // dialect.
321   WalkResult result = body.walk([&](Operation *op) -> WalkResult {
322     if (!isa_and_nonnull<PDLDialect>(op->getDialect())) {
323       emitOpError("expected only `pdl` operations within the pattern body")
324           .attachNote(op->getLoc())
325           .append("see non-`pdl` operation defined here");
326       return WalkResult::interrupt();
327     }
328     return WalkResult::advance();
329   });
330   if (result.wasInterrupted())
331     return failure();
332 
333   // Check that there is at least one operation.
334   if (body.front().getOps<OperationOp>().empty())
335     return emitOpError("the pattern must contain at least one `pdl.operation`");
336 
337   // Determine if the operations within the pdl.pattern form a connected
338   // component. This is determined by starting the search from the first
339   // operand/result/operation and visiting their users / parents / operands.
340   // We limit our attention to operations that have a user in pdl.rewrite,
341   // those that do not will be detected via other means (expected bindable
342   // user).
343   bool first = true;
344   DenseSet<Operation *> visited;
345   for (Operation &op : body.front()) {
346     // The following are the operations forming the connected component.
347     if (!isa<OperandOp, OperandsOp, ResultOp, ResultsOp, OperationOp>(op))
348       continue;
349 
350     // Determine if the operation has a user in `pdl.rewrite`.
351     bool hasUserInRewrite = false;
352     for (Operation *user : op.getUsers()) {
353       Region *region = user->getParentRegion();
354       if (isa<RewriteOp>(user) ||
355           (region && isa<RewriteOp>(region->getParentOp()))) {
356         hasUserInRewrite = true;
357         break;
358       }
359     }
360 
361     // If the operation does not have a user in `pdl.rewrite`, ignore it.
362     if (!hasUserInRewrite)
363       continue;
364 
365     if (first) {
366       // For the first operation, invoke visit.
367       visit(&op, visited);
368       first = false;
369     } else if (!visited.count(&op)) {
370       // For the subsequent operations, check if already visited.
371       return emitOpError("the operations must form a connected component")
372           .attachNote(op.getLoc())
373           .append("see a disconnected value / operation here");
374     }
375   }
376 
377   return success();
378 }
379 
380 void PatternOp::build(OpBuilder &builder, OperationState &state,
381                       Optional<uint16_t> benefit, Optional<StringRef> name) {
382   build(builder, state, builder.getI16IntegerAttr(benefit ? *benefit : 0),
383         name ? builder.getStringAttr(*name) : StringAttr());
384   state.regions[0]->emplaceBlock();
385 }
386 
387 /// Returns the rewrite operation of this pattern.
388 RewriteOp PatternOp::getRewriter() {
389   return cast<RewriteOp>(body().front().getTerminator());
390 }
391 
392 /// The default dialect is `pdl`.
393 StringRef PatternOp::getDefaultDialect() {
394   return PDLDialect::getDialectNamespace();
395 }
396 
397 //===----------------------------------------------------------------------===//
398 // pdl::ReplaceOp
399 //===----------------------------------------------------------------------===//
400 
401 LogicalResult ReplaceOp::verify() {
402   if (replOperation() && !replValues().empty())
403     return emitOpError() << "expected no replacement values to be provided"
404                             " when the replacement operation is present";
405   return success();
406 }
407 
408 //===----------------------------------------------------------------------===//
409 // pdl::ResultsOp
410 //===----------------------------------------------------------------------===//
411 
412 static ParseResult parseResultsValueType(OpAsmParser &p, IntegerAttr index,
413                                          Type &resultType) {
414   if (!index) {
415     resultType = RangeType::get(p.getBuilder().getType<ValueType>());
416     return success();
417   }
418   if (p.parseArrow() || p.parseType(resultType))
419     return failure();
420   return success();
421 }
422 
423 static void printResultsValueType(OpAsmPrinter &p, ResultsOp op,
424                                   IntegerAttr index, Type resultType) {
425   if (index)
426     p << " -> " << resultType;
427 }
428 
429 LogicalResult ResultsOp::verify() {
430   if (!index() && getType().isa<pdl::ValueType>()) {
431     return emitOpError() << "expected `pdl.range<value>` result type when "
432                             "no index is specified, but got: "
433                          << getType();
434   }
435   return success();
436 }
437 
438 //===----------------------------------------------------------------------===//
439 // pdl::RewriteOp
440 //===----------------------------------------------------------------------===//
441 
442 LogicalResult RewriteOp::verifyRegions() {
443   Region &rewriteRegion = body();
444 
445   // Handle the case where the rewrite is external.
446   if (name()) {
447     if (!rewriteRegion.empty()) {
448       return emitOpError()
449              << "expected rewrite region to be empty when rewrite is external";
450     }
451     return success();
452   }
453 
454   // Otherwise, check that the rewrite region only contains a single block.
455   if (rewriteRegion.empty()) {
456     return emitOpError() << "expected rewrite region to be non-empty if "
457                             "external name is not specified";
458   }
459 
460   // Check that no additional arguments were provided.
461   if (!externalArgs().empty()) {
462     return emitOpError() << "expected no external arguments when the "
463                             "rewrite is specified inline";
464   }
465 
466   return success();
467 }
468 
469 /// The default dialect is `pdl`.
470 StringRef RewriteOp::getDefaultDialect() {
471   return PDLDialect::getDialectNamespace();
472 }
473 
474 //===----------------------------------------------------------------------===//
475 // pdl::TypeOp
476 //===----------------------------------------------------------------------===//
477 
478 LogicalResult TypeOp::verify() {
479   if (!typeAttr())
480     return verifyHasBindingUse(*this);
481   return success();
482 }
483 
484 //===----------------------------------------------------------------------===//
485 // pdl::TypesOp
486 //===----------------------------------------------------------------------===//
487 
488 LogicalResult TypesOp::verify() {
489   if (!typesAttr())
490     return verifyHasBindingUse(*this);
491   return success();
492 }
493 
494 //===----------------------------------------------------------------------===//
495 // TableGen'd op method definitions
496 //===----------------------------------------------------------------------===//
497 
498 #define GET_OP_CLASSES
499 #include "mlir/Dialect/PDL/IR/PDLOps.cpp.inc"
500