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 ®istry) { 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 ®ion, 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 ®ion, 542 MutableArrayRef<Region> varRegions) { 543 printer.printRegion(region); 544 if (!varRegions.empty()) { 545 printer << ", "; 546 for (Region ®ion : 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 ®ion = *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 ®ion, 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> ®ions) { 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> ®ions) { 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