1 //===- TestDialect.cpp - MLIR Dialect for Testing -------------------------===//
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 "TestDialect.h"
10 #include "TestAttributes.h"
11 #include "TestInterfaces.h"
12 #include "TestTypes.h"
13 #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
14 #include "mlir/Dialect/DLTI/DLTI.h"
15 #include "mlir/Dialect/StandardOps/IR/Ops.h"
16 #include "mlir/Dialect/Tensor/IR/Tensor.h"
17 #include "mlir/IR/BuiltinOps.h"
18 #include "mlir/IR/DialectImplementation.h"
19 #include "mlir/IR/PatternMatch.h"
20 #include "mlir/IR/TypeUtilities.h"
21 #include "mlir/Reducer/ReductionPatternInterface.h"
22 #include "mlir/Transforms/FoldUtils.h"
23 #include "mlir/Transforms/InliningUtils.h"
24 #include "llvm/ADT/StringSwitch.h"
25 
26 // Include this before the using namespace lines below to
27 // test that we don't have namespace dependencies.
28 #include "TestOpsDialect.cpp.inc"
29 
30 using namespace mlir;
31 using namespace test;
32 
33 void test::registerTestDialect(DialectRegistry &registry) {
34   registry.insert<TestDialect>();
35 }
36 
37 //===----------------------------------------------------------------------===//
38 // TestDialect Interfaces
39 //===----------------------------------------------------------------------===//
40 
41 namespace {
42 
43 /// Testing the correctness of some traits.
44 static_assert(
45     llvm::is_detected<OpTrait::has_implicit_terminator_t,
46                       SingleBlockImplicitTerminatorOp>::value,
47     "has_implicit_terminator_t does not match SingleBlockImplicitTerminatorOp");
48 static_assert(OpTrait::hasSingleBlockImplicitTerminator<
49                   SingleBlockImplicitTerminatorOp>::value,
50               "hasSingleBlockImplicitTerminator does not match "
51               "SingleBlockImplicitTerminatorOp");
52 
53 // Test support for interacting with the AsmPrinter.
54 struct TestOpAsmInterface : public OpAsmDialectInterface {
55   using OpAsmDialectInterface::OpAsmDialectInterface;
56 
57   AliasResult getAlias(Attribute attr, raw_ostream &os) const final {
58     StringAttr strAttr = attr.dyn_cast<StringAttr>();
59     if (!strAttr)
60       return AliasResult::NoAlias;
61 
62     // Check the contents of the string attribute to see what the test alias
63     // should be named.
64     Optional<StringRef> aliasName =
65         StringSwitch<Optional<StringRef>>(strAttr.getValue())
66             .Case("alias_test:dot_in_name", StringRef("test.alias"))
67             .Case("alias_test:trailing_digit", StringRef("test_alias0"))
68             .Case("alias_test:prefixed_digit", StringRef("0_test_alias"))
69             .Case("alias_test:sanitize_conflict_a",
70                   StringRef("test_alias_conflict0"))
71             .Case("alias_test:sanitize_conflict_b",
72                   StringRef("test_alias_conflict0_"))
73             .Case("alias_test:tensor_encoding", StringRef("test_encoding"))
74             .Default(llvm::None);
75     if (!aliasName)
76       return AliasResult::NoAlias;
77 
78     os << *aliasName;
79     return AliasResult::FinalAlias;
80   }
81 
82   AliasResult getAlias(Type type, raw_ostream &os) const final {
83     if (auto tupleType = type.dyn_cast<TupleType>()) {
84       if (tupleType.size() > 0 &&
85           llvm::all_of(tupleType.getTypes(), [](Type elemType) {
86             return elemType.isa<SimpleAType>();
87           })) {
88         os << "test_tuple";
89         return AliasResult::FinalAlias;
90       }
91     }
92     if (auto intType = type.dyn_cast<TestIntegerType>()) {
93       if (intType.getSignedness() ==
94               TestIntegerType::SignednessSemantics::Unsigned &&
95           intType.getWidth() == 8) {
96         os << "test_ui8";
97         return AliasResult::FinalAlias;
98       }
99     }
100     return AliasResult::NoAlias;
101   }
102 };
103 
104 struct TestDialectFoldInterface : public DialectFoldInterface {
105   using DialectFoldInterface::DialectFoldInterface;
106 
107   /// Registered hook to check if the given region, which is attached to an
108   /// operation that is *not* isolated from above, should be used when
109   /// materializing constants.
110   bool shouldMaterializeInto(Region *region) const final {
111     // If this is a one region operation, then insert into it.
112     return isa<OneRegionOp>(region->getParentOp());
113   }
114 };
115 
116 /// This class defines the interface for handling inlining with standard
117 /// operations.
118 struct TestInlinerInterface : public DialectInlinerInterface {
119   using DialectInlinerInterface::DialectInlinerInterface;
120 
121   //===--------------------------------------------------------------------===//
122   // Analysis Hooks
123   //===--------------------------------------------------------------------===//
124 
125   bool isLegalToInline(Operation *call, Operation *callable,
126                        bool wouldBeCloned) const final {
127     // Don't allow inlining calls that are marked `noinline`.
128     return !call->hasAttr("noinline");
129   }
130   bool isLegalToInline(Region *, Region *, bool,
131                        BlockAndValueMapping &) const final {
132     // Inlining into test dialect regions is legal.
133     return true;
134   }
135   bool isLegalToInline(Operation *, Region *, bool,
136                        BlockAndValueMapping &) const final {
137     return true;
138   }
139 
140   bool shouldAnalyzeRecursively(Operation *op) const final {
141     // Analyze recursively if this is not a functional region operation, it
142     // froms a separate functional scope.
143     return !isa<FunctionalRegionOp>(op);
144   }
145 
146   //===--------------------------------------------------------------------===//
147   // Transformation Hooks
148   //===--------------------------------------------------------------------===//
149 
150   /// Handle the given inlined terminator by replacing it with a new operation
151   /// as necessary.
152   void handleTerminator(Operation *op,
153                         ArrayRef<Value> valuesToRepl) const final {
154     // Only handle "test.return" here.
155     auto returnOp = dyn_cast<TestReturnOp>(op);
156     if (!returnOp)
157       return;
158 
159     // Replace the values directly with the return operands.
160     assert(returnOp.getNumOperands() == valuesToRepl.size());
161     for (const auto &it : llvm::enumerate(returnOp.getOperands()))
162       valuesToRepl[it.index()].replaceAllUsesWith(it.value());
163   }
164 
165   /// Attempt to materialize a conversion for a type mismatch between a call
166   /// from this dialect, and a callable region. This method should generate an
167   /// operation that takes 'input' as the only operand, and produces a single
168   /// result of 'resultType'. If a conversion can not be generated, nullptr
169   /// should be returned.
170   Operation *materializeCallConversion(OpBuilder &builder, Value input,
171                                        Type resultType,
172                                        Location conversionLoc) const final {
173     // Only allow conversion for i16/i32 types.
174     if (!(resultType.isSignlessInteger(16) ||
175           resultType.isSignlessInteger(32)) ||
176         !(input.getType().isSignlessInteger(16) ||
177           input.getType().isSignlessInteger(32)))
178       return nullptr;
179     return builder.create<TestCastOp>(conversionLoc, resultType, input);
180   }
181 
182   void processInlinedCallBlocks(
183       Operation *call,
184       iterator_range<Region::iterator> inlinedBlocks) const final {
185     if (!isa<ConversionCallOp>(call))
186       return;
187 
188     // Set attributed on all ops in the inlined blocks.
189     for (Block &block : inlinedBlocks) {
190       block.walk([&](Operation *op) {
191         op->setAttr("inlined_conversion", UnitAttr::get(call->getContext()));
192       });
193     }
194   }
195 };
196 
197 struct TestReductionPatternInterface : public DialectReductionPatternInterface {
198 public:
199   TestReductionPatternInterface(Dialect *dialect)
200       : DialectReductionPatternInterface(dialect) {}
201 
202   void populateReductionPatterns(RewritePatternSet &patterns) const final {
203     populateTestReductionPatterns(patterns);
204   }
205 };
206 
207 } // namespace
208 
209 //===----------------------------------------------------------------------===//
210 // TestDialect
211 //===----------------------------------------------------------------------===//
212 
213 static void testSideEffectOpGetEffect(
214     Operation *op,
215     SmallVectorImpl<SideEffects::EffectInstance<TestEffects::Effect>> &effects);
216 
217 // This is the implementation of a dialect fallback for `TestEffectOpInterface`.
218 struct TestOpEffectInterfaceFallback
219     : public TestEffectOpInterface::FallbackModel<
220           TestOpEffectInterfaceFallback> {
221   static bool classof(Operation *op) {
222     bool isSupportedOp =
223         op->getName().getStringRef() == "test.unregistered_side_effect_op";
224     assert(isSupportedOp && "Unexpected dispatch");
225     return isSupportedOp;
226   }
227 
228   void
229   getEffects(Operation *op,
230              SmallVectorImpl<SideEffects::EffectInstance<TestEffects::Effect>>
231                  &effects) const {
232     testSideEffectOpGetEffect(op, effects);
233   }
234 };
235 
236 void TestDialect::initialize() {
237   registerAttributes();
238   registerTypes();
239   addOperations<
240 #define GET_OP_LIST
241 #include "TestOps.cpp.inc"
242       >();
243   addInterfaces<TestOpAsmInterface, TestDialectFoldInterface,
244                 TestInlinerInterface, TestReductionPatternInterface>();
245   allowUnknownOperations();
246 
247   // Instantiate our fallback op interface that we'll use on specific
248   // unregistered op.
249   fallbackEffectOpInterfaces = new TestOpEffectInterfaceFallback;
250 }
251 TestDialect::~TestDialect() {
252   delete static_cast<TestOpEffectInterfaceFallback *>(
253       fallbackEffectOpInterfaces);
254 }
255 
256 Operation *TestDialect::materializeConstant(OpBuilder &builder, Attribute value,
257                                             Type type, Location loc) {
258   return builder.create<TestOpConstant>(loc, type, value);
259 }
260 
261 ::mlir::LogicalResult FormatInferType2Op::inferReturnTypes(
262     ::mlir::MLIRContext *context, ::llvm::Optional<::mlir::Location> location,
263     ::mlir::ValueRange operands, ::mlir::DictionaryAttr attributes,
264     ::mlir::RegionRange regions,
265     ::llvm::SmallVectorImpl<::mlir::Type> &inferredReturnTypes) {
266   inferredReturnTypes.assign({::mlir::IntegerType::get(context, 16)});
267   return ::mlir::success();
268 }
269 
270 void *TestDialect::getRegisteredInterfaceForOp(TypeID typeID,
271                                                OperationName opName) {
272   if (opName.getIdentifier() == "test.unregistered_side_effect_op" &&
273       typeID == TypeID::get<TestEffectOpInterface>())
274     return fallbackEffectOpInterfaces;
275   return nullptr;
276 }
277 
278 LogicalResult TestDialect::verifyOperationAttribute(Operation *op,
279                                                     NamedAttribute namedAttr) {
280   if (namedAttr.getName() == "test.invalid_attr")
281     return op->emitError() << "invalid to use 'test.invalid_attr'";
282   return success();
283 }
284 
285 LogicalResult TestDialect::verifyRegionArgAttribute(Operation *op,
286                                                     unsigned regionIndex,
287                                                     unsigned argIndex,
288                                                     NamedAttribute namedAttr) {
289   if (namedAttr.getName() == "test.invalid_attr")
290     return op->emitError() << "invalid to use 'test.invalid_attr'";
291   return success();
292 }
293 
294 LogicalResult
295 TestDialect::verifyRegionResultAttribute(Operation *op, unsigned regionIndex,
296                                          unsigned resultIndex,
297                                          NamedAttribute namedAttr) {
298   if (namedAttr.getName() == "test.invalid_attr")
299     return op->emitError() << "invalid to use 'test.invalid_attr'";
300   return success();
301 }
302 
303 Optional<Dialect::ParseOpHook>
304 TestDialect::getParseOperationHook(StringRef opName) const {
305   if (opName == "test.dialect_custom_printer") {
306     return ParseOpHook{[](OpAsmParser &parser, OperationState &state) {
307       return parser.parseKeyword("custom_format");
308     }};
309   }
310   if (opName == "test.dialect_custom_format_fallback") {
311     return ParseOpHook{[](OpAsmParser &parser, OperationState &state) {
312       return parser.parseKeyword("custom_format_fallback");
313     }};
314   }
315   return None;
316 }
317 
318 llvm::unique_function<void(Operation *, OpAsmPrinter &)>
319 TestDialect::getOperationPrinter(Operation *op) const {
320   StringRef opName = op->getName().getStringRef();
321   if (opName == "test.dialect_custom_printer") {
322     return [](Operation *op, OpAsmPrinter &printer) {
323       printer.getStream() << " custom_format";
324     };
325   }
326   if (opName == "test.dialect_custom_format_fallback") {
327     return [](Operation *op, OpAsmPrinter &printer) {
328       printer.getStream() << " custom_format_fallback";
329     };
330   }
331   return {};
332 }
333 
334 //===----------------------------------------------------------------------===//
335 // TestBranchOp
336 //===----------------------------------------------------------------------===//
337 
338 Optional<MutableOperandRange>
339 TestBranchOp::getMutableSuccessorOperands(unsigned index) {
340   assert(index == 0 && "invalid successor index");
341   return getTargetOperandsMutable();
342 }
343 
344 //===----------------------------------------------------------------------===//
345 // TestDialectCanonicalizerOp
346 //===----------------------------------------------------------------------===//
347 
348 static LogicalResult
349 dialectCanonicalizationPattern(TestDialectCanonicalizerOp op,
350                                PatternRewriter &rewriter) {
351   rewriter.replaceOpWithNewOp<arith::ConstantOp>(
352       op, rewriter.getI32IntegerAttr(42));
353   return success();
354 }
355 
356 void TestDialect::getCanonicalizationPatterns(
357     RewritePatternSet &results) const {
358   results.add(&dialectCanonicalizationPattern);
359 }
360 
361 //===----------------------------------------------------------------------===//
362 // TestFoldToCallOp
363 //===----------------------------------------------------------------------===//
364 
365 namespace {
366 struct FoldToCallOpPattern : public OpRewritePattern<FoldToCallOp> {
367   using OpRewritePattern<FoldToCallOp>::OpRewritePattern;
368 
369   LogicalResult matchAndRewrite(FoldToCallOp op,
370                                 PatternRewriter &rewriter) const override {
371     rewriter.replaceOpWithNewOp<CallOp>(op, TypeRange(), op.getCalleeAttr(),
372                                         ValueRange());
373     return success();
374   }
375 };
376 } // namespace
377 
378 void FoldToCallOp::getCanonicalizationPatterns(RewritePatternSet &results,
379                                                MLIRContext *context) {
380   results.add<FoldToCallOpPattern>(context);
381 }
382 
383 //===----------------------------------------------------------------------===//
384 // Test Format* operations
385 //===----------------------------------------------------------------------===//
386 
387 //===----------------------------------------------------------------------===//
388 // Parsing
389 
390 static ParseResult parseCustomDirectiveOperands(
391     OpAsmParser &parser, OpAsmParser::OperandType &operand,
392     Optional<OpAsmParser::OperandType> &optOperand,
393     SmallVectorImpl<OpAsmParser::OperandType> &varOperands) {
394   if (parser.parseOperand(operand))
395     return failure();
396   if (succeeded(parser.parseOptionalComma())) {
397     optOperand.emplace();
398     if (parser.parseOperand(*optOperand))
399       return failure();
400   }
401   if (parser.parseArrow() || parser.parseLParen() ||
402       parser.parseOperandList(varOperands) || parser.parseRParen())
403     return failure();
404   return success();
405 }
406 static ParseResult
407 parseCustomDirectiveResults(OpAsmParser &parser, Type &operandType,
408                             Type &optOperandType,
409                             SmallVectorImpl<Type> &varOperandTypes) {
410   if (parser.parseColon())
411     return failure();
412 
413   if (parser.parseType(operandType))
414     return failure();
415   if (succeeded(parser.parseOptionalComma())) {
416     if (parser.parseType(optOperandType))
417       return failure();
418   }
419   if (parser.parseArrow() || parser.parseLParen() ||
420       parser.parseTypeList(varOperandTypes) || parser.parseRParen())
421     return failure();
422   return success();
423 }
424 static ParseResult
425 parseCustomDirectiveWithTypeRefs(OpAsmParser &parser, Type operandType,
426                                  Type optOperandType,
427                                  const SmallVectorImpl<Type> &varOperandTypes) {
428   if (parser.parseKeyword("type_refs_capture"))
429     return failure();
430 
431   Type operandType2, optOperandType2;
432   SmallVector<Type, 1> varOperandTypes2;
433   if (parseCustomDirectiveResults(parser, operandType2, optOperandType2,
434                                   varOperandTypes2))
435     return failure();
436 
437   if (operandType != operandType2 || optOperandType != optOperandType2 ||
438       varOperandTypes != varOperandTypes2)
439     return failure();
440 
441   return success();
442 }
443 static ParseResult parseCustomDirectiveOperandsAndTypes(
444     OpAsmParser &parser, OpAsmParser::OperandType &operand,
445     Optional<OpAsmParser::OperandType> &optOperand,
446     SmallVectorImpl<OpAsmParser::OperandType> &varOperands, Type &operandType,
447     Type &optOperandType, SmallVectorImpl<Type> &varOperandTypes) {
448   if (parseCustomDirectiveOperands(parser, operand, optOperand, varOperands) ||
449       parseCustomDirectiveResults(parser, operandType, optOperandType,
450                                   varOperandTypes))
451     return failure();
452   return success();
453 }
454 static ParseResult parseCustomDirectiveRegions(
455     OpAsmParser &parser, Region &region,
456     SmallVectorImpl<std::unique_ptr<Region>> &varRegions) {
457   if (parser.parseRegion(region))
458     return failure();
459   if (failed(parser.parseOptionalComma()))
460     return success();
461   std::unique_ptr<Region> varRegion = std::make_unique<Region>();
462   if (parser.parseRegion(*varRegion))
463     return failure();
464   varRegions.emplace_back(std::move(varRegion));
465   return success();
466 }
467 static ParseResult
468 parseCustomDirectiveSuccessors(OpAsmParser &parser, Block *&successor,
469                                SmallVectorImpl<Block *> &varSuccessors) {
470   if (parser.parseSuccessor(successor))
471     return failure();
472   if (failed(parser.parseOptionalComma()))
473     return success();
474   Block *varSuccessor;
475   if (parser.parseSuccessor(varSuccessor))
476     return failure();
477   varSuccessors.append(2, varSuccessor);
478   return success();
479 }
480 static ParseResult parseCustomDirectiveAttributes(OpAsmParser &parser,
481                                                   IntegerAttr &attr,
482                                                   IntegerAttr &optAttr) {
483   if (parser.parseAttribute(attr))
484     return failure();
485   if (succeeded(parser.parseOptionalComma())) {
486     if (parser.parseAttribute(optAttr))
487       return failure();
488   }
489   return success();
490 }
491 
492 static ParseResult parseCustomDirectiveAttrDict(OpAsmParser &parser,
493                                                 NamedAttrList &attrs) {
494   return parser.parseOptionalAttrDict(attrs);
495 }
496 static ParseResult parseCustomDirectiveOptionalOperandRef(
497     OpAsmParser &parser, Optional<OpAsmParser::OperandType> &optOperand) {
498   int64_t operandCount = 0;
499   if (parser.parseInteger(operandCount))
500     return failure();
501   bool expectedOptionalOperand = operandCount == 0;
502   return success(expectedOptionalOperand != optOperand.hasValue());
503 }
504 
505 //===----------------------------------------------------------------------===//
506 // Printing
507 
508 static void printCustomDirectiveOperands(OpAsmPrinter &printer, Operation *,
509                                          Value operand, Value optOperand,
510                                          OperandRange varOperands) {
511   printer << operand;
512   if (optOperand)
513     printer << ", " << optOperand;
514   printer << " -> (" << varOperands << ")";
515 }
516 static void printCustomDirectiveResults(OpAsmPrinter &printer, Operation *,
517                                         Type operandType, Type optOperandType,
518                                         TypeRange varOperandTypes) {
519   printer << " : " << operandType;
520   if (optOperandType)
521     printer << ", " << optOperandType;
522   printer << " -> (" << varOperandTypes << ")";
523 }
524 static void printCustomDirectiveWithTypeRefs(OpAsmPrinter &printer,
525                                              Operation *op, Type operandType,
526                                              Type optOperandType,
527                                              TypeRange varOperandTypes) {
528   printer << " type_refs_capture ";
529   printCustomDirectiveResults(printer, op, operandType, optOperandType,
530                               varOperandTypes);
531 }
532 static void printCustomDirectiveOperandsAndTypes(
533     OpAsmPrinter &printer, Operation *op, Value operand, Value optOperand,
534     OperandRange varOperands, Type operandType, Type optOperandType,
535     TypeRange varOperandTypes) {
536   printCustomDirectiveOperands(printer, op, operand, optOperand, varOperands);
537   printCustomDirectiveResults(printer, op, operandType, optOperandType,
538                               varOperandTypes);
539 }
540 static void printCustomDirectiveRegions(OpAsmPrinter &printer, Operation *,
541                                         Region &region,
542                                         MutableArrayRef<Region> varRegions) {
543   printer.printRegion(region);
544   if (!varRegions.empty()) {
545     printer << ", ";
546     for (Region &region : varRegions)
547       printer.printRegion(region);
548   }
549 }
550 static void printCustomDirectiveSuccessors(OpAsmPrinter &printer, Operation *,
551                                            Block *successor,
552                                            SuccessorRange varSuccessors) {
553   printer << successor;
554   if (!varSuccessors.empty())
555     printer << ", " << varSuccessors.front();
556 }
557 static void printCustomDirectiveAttributes(OpAsmPrinter &printer, Operation *,
558                                            Attribute attribute,
559                                            Attribute optAttribute) {
560   printer << attribute;
561   if (optAttribute)
562     printer << ", " << optAttribute;
563 }
564 
565 static void printCustomDirectiveAttrDict(OpAsmPrinter &printer, Operation *op,
566                                          DictionaryAttr attrs) {
567   printer.printOptionalAttrDict(attrs.getValue());
568 }
569 
570 static void printCustomDirectiveOptionalOperandRef(OpAsmPrinter &printer,
571                                                    Operation *op,
572                                                    Value optOperand) {
573   printer << (optOperand ? "1" : "0");
574 }
575 
576 //===----------------------------------------------------------------------===//
577 // Test IsolatedRegionOp - parse passthrough region arguments.
578 //===----------------------------------------------------------------------===//
579 
580 static ParseResult parseIsolatedRegionOp(OpAsmParser &parser,
581                                          OperationState &result) {
582   OpAsmParser::OperandType argInfo;
583   Type argType = parser.getBuilder().getIndexType();
584 
585   // Parse the input operand.
586   if (parser.parseOperand(argInfo) ||
587       parser.resolveOperand(argInfo, argType, result.operands))
588     return failure();
589 
590   // Parse the body region, and reuse the operand info as the argument info.
591   Region *body = result.addRegion();
592   return parser.parseRegion(*body, argInfo, argType, /*argLocations=*/{},
593                             /*enableNameShadowing=*/true);
594 }
595 
596 static void print(OpAsmPrinter &p, IsolatedRegionOp op) {
597   p << "test.isolated_region ";
598   p.printOperand(op.getOperand());
599   p.shadowRegionArgs(op.getRegion(), op.getOperand());
600   p << ' ';
601   p.printRegion(op.getRegion(), /*printEntryBlockArgs=*/false);
602 }
603 
604 //===----------------------------------------------------------------------===//
605 // Test SSACFGRegionOp
606 //===----------------------------------------------------------------------===//
607 
608 RegionKind SSACFGRegionOp::getRegionKind(unsigned index) {
609   return RegionKind::SSACFG;
610 }
611 
612 //===----------------------------------------------------------------------===//
613 // Test GraphRegionOp
614 //===----------------------------------------------------------------------===//
615 
616 static ParseResult parseGraphRegionOp(OpAsmParser &parser,
617                                       OperationState &result) {
618   // Parse the body region, and reuse the operand info as the argument info.
619   Region *body = result.addRegion();
620   return parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{});
621 }
622 
623 static void print(OpAsmPrinter &p, GraphRegionOp op) {
624   p << "test.graph_region ";
625   p.printRegion(op.getRegion(), /*printEntryBlockArgs=*/false);
626 }
627 
628 RegionKind GraphRegionOp::getRegionKind(unsigned index) {
629   return RegionKind::Graph;
630 }
631 
632 //===----------------------------------------------------------------------===//
633 // Test AffineScopeOp
634 //===----------------------------------------------------------------------===//
635 
636 static ParseResult parseAffineScopeOp(OpAsmParser &parser,
637                                       OperationState &result) {
638   // Parse the body region, and reuse the operand info as the argument info.
639   Region *body = result.addRegion();
640   return parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{});
641 }
642 
643 static void print(OpAsmPrinter &p, AffineScopeOp op) {
644   p << "test.affine_scope ";
645   p.printRegion(op.getRegion(), /*printEntryBlockArgs=*/false);
646 }
647 
648 //===----------------------------------------------------------------------===//
649 // Test parser.
650 //===----------------------------------------------------------------------===//
651 
652 static ParseResult parseParseIntegerLiteralOp(OpAsmParser &parser,
653                                               OperationState &result) {
654   if (parser.parseOptionalColon())
655     return success();
656   uint64_t numResults;
657   if (parser.parseInteger(numResults))
658     return failure();
659 
660   IndexType type = parser.getBuilder().getIndexType();
661   for (unsigned i = 0; i < numResults; ++i)
662     result.addTypes(type);
663   return success();
664 }
665 
666 static void print(OpAsmPrinter &p, ParseIntegerLiteralOp op) {
667   if (unsigned numResults = op->getNumResults())
668     p << " : " << numResults;
669 }
670 
671 static ParseResult parseParseWrappedKeywordOp(OpAsmParser &parser,
672                                               OperationState &result) {
673   StringRef keyword;
674   if (parser.parseKeyword(&keyword))
675     return failure();
676   result.addAttribute("keyword", parser.getBuilder().getStringAttr(keyword));
677   return success();
678 }
679 
680 static void print(OpAsmPrinter &p, ParseWrappedKeywordOp op) {
681   p << " " << op.getKeyword();
682 }
683 
684 //===----------------------------------------------------------------------===//
685 // Test WrapRegionOp - wrapping op exercising `parseGenericOperation()`.
686 
687 static ParseResult parseWrappingRegionOp(OpAsmParser &parser,
688                                          OperationState &result) {
689   if (parser.parseKeyword("wraps"))
690     return failure();
691 
692   // Parse the wrapped op in a region
693   Region &body = *result.addRegion();
694   body.push_back(new Block);
695   Block &block = body.back();
696   Operation *wrappedOp = parser.parseGenericOperation(&block, block.begin());
697   if (!wrappedOp)
698     return failure();
699 
700   // Create a return terminator in the inner region, pass as operand to the
701   // terminator the returned values from the wrapped operation.
702   SmallVector<Value, 8> returnOperands(wrappedOp->getResults());
703   OpBuilder builder(parser.getContext());
704   builder.setInsertionPointToEnd(&block);
705   builder.create<TestReturnOp>(wrappedOp->getLoc(), returnOperands);
706 
707   // Get the results type for the wrapping op from the terminator operands.
708   Operation &returnOp = body.back().back();
709   result.types.append(returnOp.operand_type_begin(),
710                       returnOp.operand_type_end());
711 
712   // Use the location of the wrapped op for the "test.wrapping_region" op.
713   result.location = wrappedOp->getLoc();
714 
715   return success();
716 }
717 
718 static void print(OpAsmPrinter &p, WrappingRegionOp op) {
719   p << " wraps ";
720   p.printGenericOp(&op.getRegion().front().front());
721 }
722 
723 //===----------------------------------------------------------------------===//
724 // Test PrettyPrintedRegionOp -  exercising the following parser APIs
725 //   parseGenericOperationAfterOpName
726 //   parseCustomOperationName
727 //===----------------------------------------------------------------------===//
728 
729 static ParseResult parsePrettyPrintedRegionOp(OpAsmParser &parser,
730                                               OperationState &result) {
731 
732   SMLoc loc = parser.getCurrentLocation();
733   Location currLocation = parser.getEncodedSourceLoc(loc);
734 
735   // Parse the operands.
736   SmallVector<OpAsmParser::OperandType, 2> operands;
737   if (parser.parseOperandList(operands))
738     return failure();
739 
740   // Check if we are parsing the pretty-printed version
741   //  test.pretty_printed_region start <inner-op> end : <functional-type>
742   // Else fallback to parsing the "non pretty-printed" version.
743   if (!succeeded(parser.parseOptionalKeyword("start")))
744     return parser.parseGenericOperationAfterOpName(
745         result, llvm::makeArrayRef(operands));
746 
747   FailureOr<OperationName> parseOpNameInfo = parser.parseCustomOperationName();
748   if (failed(parseOpNameInfo))
749     return failure();
750 
751   StringRef innerOpName = parseOpNameInfo->getStringRef();
752 
753   FunctionType opFntype;
754   Optional<Location> explicitLoc;
755   if (parser.parseKeyword("end") || parser.parseColon() ||
756       parser.parseType(opFntype) ||
757       parser.parseOptionalLocationSpecifier(explicitLoc))
758     return failure();
759 
760   // If location of the op is explicitly provided, then use it; Else use
761   // the parser's current location.
762   Location opLoc = explicitLoc.getValueOr(currLocation);
763 
764   // Derive the SSA-values for op's operands.
765   if (parser.resolveOperands(operands, opFntype.getInputs(), loc,
766                              result.operands))
767     return failure();
768 
769   // Add a region for op.
770   Region &region = *result.addRegion();
771 
772   // Create a basic-block inside op's region.
773   Block &block = region.emplaceBlock();
774 
775   // Create and insert an "inner-op" operation in the block.
776   // Just for testing purposes, we can assume that inner op is a binary op with
777   // result and operand types all same as the test-op's first operand.
778   Type innerOpType = opFntype.getInput(0);
779   Value lhs = block.addArgument(innerOpType, opLoc);
780   Value rhs = block.addArgument(innerOpType, opLoc);
781 
782   OpBuilder builder(parser.getBuilder().getContext());
783   builder.setInsertionPointToStart(&block);
784 
785   OperationState innerOpState(opLoc, innerOpName);
786   innerOpState.operands.push_back(lhs);
787   innerOpState.operands.push_back(rhs);
788   innerOpState.addTypes(innerOpType);
789 
790   Operation *innerOp = builder.createOperation(innerOpState);
791 
792   // Insert a return statement in the block returning the inner-op's result.
793   builder.create<TestReturnOp>(innerOp->getLoc(), innerOp->getResults());
794 
795   // Populate the op operation-state with result-type and location.
796   result.addTypes(opFntype.getResults());
797   result.location = innerOp->getLoc();
798 
799   return success();
800 }
801 
802 static void print(OpAsmPrinter &p, PrettyPrintedRegionOp op) {
803   p << ' ';
804   p.printOperands(op.getOperands());
805 
806   Operation &innerOp = op.getRegion().front().front();
807   // Assuming that region has a single non-terminator inner-op, if the inner-op
808   // meets some criteria (which in this case is a simple one  based on the name
809   // of inner-op), then we can print the entire region in a succinct way.
810   // Here we assume that the prototype of "special.op" can be trivially derived
811   // while parsing it back.
812   if (innerOp.getName().getStringRef().equals("special.op")) {
813     p << " start special.op end";
814   } else {
815     p << " (";
816     p.printRegion(op.getRegion());
817     p << ")";
818   }
819 
820   p << " : ";
821   p.printFunctionalType(op);
822 }
823 
824 //===----------------------------------------------------------------------===//
825 // Test PolyForOp - parse list of region arguments.
826 //===----------------------------------------------------------------------===//
827 
828 static ParseResult parsePolyForOp(OpAsmParser &parser, OperationState &result) {
829   SmallVector<OpAsmParser::OperandType, 4> ivsInfo;
830   // Parse list of region arguments without a delimiter.
831   if (parser.parseRegionArgumentList(ivsInfo))
832     return failure();
833 
834   // Parse the body region.
835   Region *body = result.addRegion();
836   auto &builder = parser.getBuilder();
837   SmallVector<Type, 4> argTypes(ivsInfo.size(), builder.getIndexType());
838   return parser.parseRegion(*body, ivsInfo, argTypes);
839 }
840 
841 void PolyForOp::getAsmBlockArgumentNames(Region &region,
842                                          OpAsmSetValueNameFn setNameFn) {
843   auto arrayAttr = getOperation()->getAttrOfType<ArrayAttr>("arg_names");
844   if (!arrayAttr)
845     return;
846   auto args = getRegion().front().getArguments();
847   auto e = std::min(arrayAttr.size(), args.size());
848   for (unsigned i = 0; i < e; ++i) {
849     if (auto strAttr = arrayAttr[i].dyn_cast<StringAttr>())
850       setNameFn(args[i], strAttr.getValue());
851   }
852 }
853 
854 //===----------------------------------------------------------------------===//
855 // Test removing op with inner ops.
856 //===----------------------------------------------------------------------===//
857 
858 namespace {
859 struct TestRemoveOpWithInnerOps
860     : public OpRewritePattern<TestOpWithRegionPattern> {
861   using OpRewritePattern<TestOpWithRegionPattern>::OpRewritePattern;
862 
863   void initialize() { setDebugName("TestRemoveOpWithInnerOps"); }
864 
865   LogicalResult matchAndRewrite(TestOpWithRegionPattern op,
866                                 PatternRewriter &rewriter) const override {
867     rewriter.eraseOp(op);
868     return success();
869   }
870 };
871 } // namespace
872 
873 void TestOpWithRegionPattern::getCanonicalizationPatterns(
874     RewritePatternSet &results, MLIRContext *context) {
875   results.add<TestRemoveOpWithInnerOps>(context);
876 }
877 
878 OpFoldResult TestOpWithRegionFold::fold(ArrayRef<Attribute> operands) {
879   return getOperand();
880 }
881 
882 OpFoldResult TestOpConstant::fold(ArrayRef<Attribute> operands) {
883   return getValue();
884 }
885 
886 LogicalResult TestOpWithVariadicResultsAndFolder::fold(
887     ArrayRef<Attribute> operands, SmallVectorImpl<OpFoldResult> &results) {
888   for (Value input : this->getOperands()) {
889     results.push_back(input);
890   }
891   return success();
892 }
893 
894 OpFoldResult TestOpInPlaceFold::fold(ArrayRef<Attribute> operands) {
895   assert(operands.size() == 1);
896   if (operands.front()) {
897     (*this)->setAttr("attr", operands.front());
898     return getResult();
899   }
900   return {};
901 }
902 
903 OpFoldResult TestPassthroughFold::fold(ArrayRef<Attribute> operands) {
904   return getOperand();
905 }
906 
907 LogicalResult OpWithInferTypeInterfaceOp::inferReturnTypes(
908     MLIRContext *, Optional<Location> location, ValueRange operands,
909     DictionaryAttr attributes, RegionRange regions,
910     SmallVectorImpl<Type> &inferredReturnTypes) {
911   if (operands[0].getType() != operands[1].getType()) {
912     return emitOptionalError(location, "operand type mismatch ",
913                              operands[0].getType(), " vs ",
914                              operands[1].getType());
915   }
916   inferredReturnTypes.assign({operands[0].getType()});
917   return success();
918 }
919 
920 LogicalResult OpWithShapedTypeInferTypeInterfaceOp::inferReturnTypeComponents(
921     MLIRContext *context, Optional<Location> location, ValueShapeRange operands,
922     DictionaryAttr attributes, RegionRange regions,
923     SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
924   // Create return type consisting of the last element of the first operand.
925   auto operandType = operands.front().getType();
926   auto sval = operandType.dyn_cast<ShapedType>();
927   if (!sval) {
928     return emitOptionalError(location, "only shaped type operands allowed");
929   }
930   int64_t dim =
931       sval.hasRank() ? sval.getShape().front() : ShapedType::kDynamicSize;
932   auto type = IntegerType::get(context, 17);
933   inferredReturnShapes.push_back(ShapedTypeComponents({dim}, type));
934   return success();
935 }
936 
937 LogicalResult OpWithShapedTypeInferTypeInterfaceOp::reifyReturnTypeShapes(
938     OpBuilder &builder, ValueRange operands,
939     llvm::SmallVectorImpl<Value> &shapes) {
940   shapes = SmallVector<Value, 1>{
941       builder.createOrFold<tensor::DimOp>(getLoc(), operands.front(), 0)};
942   return success();
943 }
944 
945 LogicalResult OpWithResultShapeInterfaceOp::reifyReturnTypeShapes(
946     OpBuilder &builder, ValueRange operands,
947     llvm::SmallVectorImpl<Value> &shapes) {
948   Location loc = getLoc();
949   shapes.reserve(operands.size());
950   for (Value operand : llvm::reverse(operands)) {
951     auto rank = operand.getType().cast<RankedTensorType>().getRank();
952     auto currShape = llvm::to_vector<4>(
953         llvm::map_range(llvm::seq<int64_t>(0, rank), [&](int64_t dim) -> Value {
954           return builder.createOrFold<tensor::DimOp>(loc, operand, dim);
955         }));
956     shapes.push_back(builder.create<tensor::FromElementsOp>(
957         getLoc(), RankedTensorType::get({rank}, builder.getIndexType()),
958         currShape));
959   }
960   return success();
961 }
962 
963 LogicalResult OpWithResultShapePerDimInterfaceOp::reifyResultShapes(
964     OpBuilder &builder, ReifiedRankedShapedTypeDims &shapes) {
965   Location loc = getLoc();
966   shapes.reserve(getNumOperands());
967   for (Value operand : llvm::reverse(getOperands())) {
968     auto currShape = llvm::to_vector<4>(llvm::map_range(
969         llvm::seq<int64_t>(
970             0, operand.getType().cast<RankedTensorType>().getRank()),
971         [&](int64_t dim) -> Value {
972           return builder.createOrFold<tensor::DimOp>(loc, operand, dim);
973         }));
974     shapes.emplace_back(std::move(currShape));
975   }
976   return success();
977 }
978 
979 //===----------------------------------------------------------------------===//
980 // Test SideEffect interfaces
981 //===----------------------------------------------------------------------===//
982 
983 namespace {
984 /// A test resource for side effects.
985 struct TestResource : public SideEffects::Resource::Base<TestResource> {
986   StringRef getName() final { return "<Test>"; }
987 };
988 } // namespace
989 
990 static void testSideEffectOpGetEffect(
991     Operation *op,
992     SmallVectorImpl<SideEffects::EffectInstance<TestEffects::Effect>>
993         &effects) {
994   auto effectsAttr = op->getAttrOfType<AffineMapAttr>("effect_parameter");
995   if (!effectsAttr)
996     return;
997 
998   effects.emplace_back(TestEffects::Concrete::get(), effectsAttr);
999 }
1000 
1001 void SideEffectOp::getEffects(
1002     SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
1003   // Check for an effects attribute on the op instance.
1004   ArrayAttr effectsAttr = (*this)->getAttrOfType<ArrayAttr>("effects");
1005   if (!effectsAttr)
1006     return;
1007 
1008   // If there is one, it is an array of dictionary attributes that hold
1009   // information on the effects of this operation.
1010   for (Attribute element : effectsAttr) {
1011     DictionaryAttr effectElement = element.cast<DictionaryAttr>();
1012 
1013     // Get the specific memory effect.
1014     MemoryEffects::Effect *effect =
1015         StringSwitch<MemoryEffects::Effect *>(
1016             effectElement.get("effect").cast<StringAttr>().getValue())
1017             .Case("allocate", MemoryEffects::Allocate::get())
1018             .Case("free", MemoryEffects::Free::get())
1019             .Case("read", MemoryEffects::Read::get())
1020             .Case("write", MemoryEffects::Write::get());
1021 
1022     // Check for a non-default resource to use.
1023     SideEffects::Resource *resource = SideEffects::DefaultResource::get();
1024     if (effectElement.get("test_resource"))
1025       resource = TestResource::get();
1026 
1027     // Check for a result to affect.
1028     if (effectElement.get("on_result"))
1029       effects.emplace_back(effect, getResult(), resource);
1030     else if (Attribute ref = effectElement.get("on_reference"))
1031       effects.emplace_back(effect, ref.cast<SymbolRefAttr>(), resource);
1032     else
1033       effects.emplace_back(effect, resource);
1034   }
1035 }
1036 
1037 void SideEffectOp::getEffects(
1038     SmallVectorImpl<TestEffects::EffectInstance> &effects) {
1039   testSideEffectOpGetEffect(getOperation(), effects);
1040 }
1041 
1042 //===----------------------------------------------------------------------===//
1043 // StringAttrPrettyNameOp
1044 //===----------------------------------------------------------------------===//
1045 
1046 // This op has fancy handling of its SSA result name.
1047 static ParseResult parseStringAttrPrettyNameOp(OpAsmParser &parser,
1048                                                OperationState &result) {
1049   // Add the result types.
1050   for (size_t i = 0, e = parser.getNumResults(); i != e; ++i)
1051     result.addTypes(parser.getBuilder().getIntegerType(32));
1052 
1053   if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
1054     return failure();
1055 
1056   // If the attribute dictionary contains no 'names' attribute, infer it from
1057   // the SSA name (if specified).
1058   bool hadNames = llvm::any_of(result.attributes, [](NamedAttribute attr) {
1059     return attr.getName() == "names";
1060   });
1061 
1062   // If there was no name specified, check to see if there was a useful name
1063   // specified in the asm file.
1064   if (hadNames || parser.getNumResults() == 0)
1065     return success();
1066 
1067   SmallVector<StringRef, 4> names;
1068   auto *context = result.getContext();
1069 
1070   for (size_t i = 0, e = parser.getNumResults(); i != e; ++i) {
1071     auto resultName = parser.getResultName(i);
1072     StringRef nameStr;
1073     if (!resultName.first.empty() && !isdigit(resultName.first[0]))
1074       nameStr = resultName.first;
1075 
1076     names.push_back(nameStr);
1077   }
1078 
1079   auto namesAttr = parser.getBuilder().getStrArrayAttr(names);
1080   result.attributes.push_back({StringAttr::get(context, "names"), namesAttr});
1081   return success();
1082 }
1083 
1084 static void print(OpAsmPrinter &p, StringAttrPrettyNameOp op) {
1085   // Note that we only need to print the "name" attribute if the asmprinter
1086   // result name disagrees with it.  This can happen in strange cases, e.g.
1087   // when there are conflicts.
1088   bool namesDisagree = op.getNames().size() != op.getNumResults();
1089 
1090   SmallString<32> resultNameStr;
1091   for (size_t i = 0, e = op.getNumResults(); i != e && !namesDisagree; ++i) {
1092     resultNameStr.clear();
1093     llvm::raw_svector_ostream tmpStream(resultNameStr);
1094     p.printOperand(op.getResult(i), tmpStream);
1095 
1096     auto expectedName = op.getNames()[i].dyn_cast<StringAttr>();
1097     if (!expectedName ||
1098         tmpStream.str().drop_front() != expectedName.getValue()) {
1099       namesDisagree = true;
1100     }
1101   }
1102 
1103   if (namesDisagree)
1104     p.printOptionalAttrDictWithKeyword(op->getAttrs());
1105   else
1106     p.printOptionalAttrDictWithKeyword(op->getAttrs(), {"names"});
1107 }
1108 
1109 // We set the SSA name in the asm syntax to the contents of the name
1110 // attribute.
1111 void StringAttrPrettyNameOp::getAsmResultNames(
1112     function_ref<void(Value, StringRef)> setNameFn) {
1113 
1114   auto value = getNames();
1115   for (size_t i = 0, e = value.size(); i != e; ++i)
1116     if (auto str = value[i].dyn_cast<StringAttr>())
1117       if (!str.getValue().empty())
1118         setNameFn(getResult(i), str.getValue());
1119 }
1120 
1121 //===----------------------------------------------------------------------===//
1122 // ResultTypeWithTraitOp
1123 //===----------------------------------------------------------------------===//
1124 
1125 LogicalResult ResultTypeWithTraitOp::verify() {
1126   if ((*this)->getResultTypes()[0].hasTrait<TypeTrait::TestTypeTrait>())
1127     return success();
1128   return emitError("result type should have trait 'TestTypeTrait'");
1129 }
1130 
1131 //===----------------------------------------------------------------------===//
1132 // AttrWithTraitOp
1133 //===----------------------------------------------------------------------===//
1134 
1135 LogicalResult AttrWithTraitOp::verify() {
1136   if (getAttr().hasTrait<AttributeTrait::TestAttrTrait>())
1137     return success();
1138   return emitError("'attr' attribute should have trait 'TestAttrTrait'");
1139 }
1140 
1141 //===----------------------------------------------------------------------===//
1142 // RegionIfOp
1143 //===----------------------------------------------------------------------===//
1144 
1145 static void print(OpAsmPrinter &p, RegionIfOp op) {
1146   p << " ";
1147   p.printOperands(op.getOperands());
1148   p << ": " << op.getOperandTypes();
1149   p.printArrowTypeList(op.getResultTypes());
1150   p << " then ";
1151   p.printRegion(op.getThenRegion(),
1152                 /*printEntryBlockArgs=*/true,
1153                 /*printBlockTerminators=*/true);
1154   p << " else ";
1155   p.printRegion(op.getElseRegion(),
1156                 /*printEntryBlockArgs=*/true,
1157                 /*printBlockTerminators=*/true);
1158   p << " join ";
1159   p.printRegion(op.getJoinRegion(),
1160                 /*printEntryBlockArgs=*/true,
1161                 /*printBlockTerminators=*/true);
1162 }
1163 
1164 static ParseResult parseRegionIfOp(OpAsmParser &parser,
1165                                    OperationState &result) {
1166   SmallVector<OpAsmParser::OperandType, 2> operandInfos;
1167   SmallVector<Type, 2> operandTypes;
1168 
1169   result.regions.reserve(3);
1170   Region *thenRegion = result.addRegion();
1171   Region *elseRegion = result.addRegion();
1172   Region *joinRegion = result.addRegion();
1173 
1174   // Parse operand, type and arrow type lists.
1175   if (parser.parseOperandList(operandInfos) ||
1176       parser.parseColonTypeList(operandTypes) ||
1177       parser.parseArrowTypeList(result.types))
1178     return failure();
1179 
1180   // Parse all attached regions.
1181   if (parser.parseKeyword("then") || parser.parseRegion(*thenRegion, {}, {}) ||
1182       parser.parseKeyword("else") || parser.parseRegion(*elseRegion, {}, {}) ||
1183       parser.parseKeyword("join") || parser.parseRegion(*joinRegion, {}, {}))
1184     return failure();
1185 
1186   return parser.resolveOperands(operandInfos, operandTypes,
1187                                 parser.getCurrentLocation(), result.operands);
1188 }
1189 
1190 OperandRange RegionIfOp::getSuccessorEntryOperands(unsigned index) {
1191   assert(index < 2 && "invalid region index");
1192   return getOperands();
1193 }
1194 
1195 void RegionIfOp::getSuccessorRegions(
1196     Optional<unsigned> index, ArrayRef<Attribute> operands,
1197     SmallVectorImpl<RegionSuccessor> &regions) {
1198   // We always branch to the join region.
1199   if (index.hasValue()) {
1200     if (index.getValue() < 2)
1201       regions.push_back(RegionSuccessor(&getJoinRegion(), getJoinArgs()));
1202     else
1203       regions.push_back(RegionSuccessor(getResults()));
1204     return;
1205   }
1206 
1207   // The then and else regions are the entry regions of this op.
1208   regions.push_back(RegionSuccessor(&getThenRegion(), getThenArgs()));
1209   regions.push_back(RegionSuccessor(&getElseRegion(), getElseArgs()));
1210 }
1211 
1212 void RegionIfOp::getRegionInvocationBounds(
1213     ArrayRef<Attribute> operands,
1214     SmallVectorImpl<InvocationBounds> &invocationBounds) {
1215   // Each region is invoked at most once.
1216   invocationBounds.assign(/*NumElts=*/3, /*Elt=*/{0, 1});
1217 }
1218 
1219 //===----------------------------------------------------------------------===//
1220 // AnyCondOp
1221 //===----------------------------------------------------------------------===//
1222 
1223 void AnyCondOp::getSuccessorRegions(Optional<unsigned> index,
1224                                     ArrayRef<Attribute> operands,
1225                                     SmallVectorImpl<RegionSuccessor> &regions) {
1226   // The parent op branches into the only region, and the region branches back
1227   // to the parent op.
1228   if (index)
1229     regions.emplace_back(&getRegion());
1230   else
1231     regions.emplace_back(getResults());
1232 }
1233 
1234 void AnyCondOp::getRegionInvocationBounds(
1235     ArrayRef<Attribute> operands,
1236     SmallVectorImpl<InvocationBounds> &invocationBounds) {
1237   invocationBounds.emplace_back(1, 1);
1238 }
1239 
1240 //===----------------------------------------------------------------------===//
1241 // SingleNoTerminatorCustomAsmOp
1242 //===----------------------------------------------------------------------===//
1243 
1244 static ParseResult parseSingleNoTerminatorCustomAsmOp(OpAsmParser &parser,
1245                                                       OperationState &state) {
1246   Region *body = state.addRegion();
1247   if (parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{}))
1248     return failure();
1249   return success();
1250 }
1251 
1252 static void print(SingleNoTerminatorCustomAsmOp op, OpAsmPrinter &printer) {
1253   printer.printRegion(
1254       op.getRegion(), /*printEntryBlockArgs=*/false,
1255       // This op has a single block without terminators. But explicitly mark
1256       // as not printing block terminators for testing.
1257       /*printBlockTerminators=*/false);
1258 }
1259 
1260 #include "TestOpEnums.cpp.inc"
1261 #include "TestOpInterfaces.cpp.inc"
1262 #include "TestOpStructs.cpp.inc"
1263 #include "TestTypeInterfaces.cpp.inc"
1264 
1265 #define GET_OP_CLASSES
1266 #include "TestOps.cpp.inc"
1267