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 "TestTypes.h" 11 #include "mlir/Dialect/StandardOps/IR/Ops.h" 12 #include "mlir/IR/DialectImplementation.h" 13 #include "mlir/IR/Function.h" 14 #include "mlir/IR/Module.h" 15 #include "mlir/IR/PatternMatch.h" 16 #include "mlir/IR/TypeUtilities.h" 17 #include "mlir/Transforms/FoldUtils.h" 18 #include "mlir/Transforms/InliningUtils.h" 19 #include "llvm/ADT/StringSwitch.h" 20 21 using namespace mlir; 22 23 //===----------------------------------------------------------------------===// 24 // TestDialect Interfaces 25 //===----------------------------------------------------------------------===// 26 27 namespace { 28 29 // Test support for interacting with the AsmPrinter. 30 struct TestOpAsmInterface : public OpAsmDialectInterface { 31 using OpAsmDialectInterface::OpAsmDialectInterface; 32 33 void getAsmResultNames(Operation *op, 34 OpAsmSetValueNameFn setNameFn) const final { 35 if (auto asmOp = dyn_cast<AsmDialectInterfaceOp>(op)) 36 setNameFn(asmOp, "result"); 37 } 38 39 void getAsmBlockArgumentNames(Block *block, 40 OpAsmSetValueNameFn setNameFn) const final { 41 auto op = block->getParentOp(); 42 auto arrayAttr = op->getAttrOfType<ArrayAttr>("arg_names"); 43 if (!arrayAttr) 44 return; 45 auto args = block->getArguments(); 46 auto e = std::min(arrayAttr.size(), args.size()); 47 for (unsigned i = 0; i < e; ++i) { 48 if (auto strAttr = arrayAttr[i].dyn_cast<StringAttr>()) 49 setNameFn(args[i], strAttr.getValue()); 50 } 51 } 52 }; 53 54 struct TestOpFolderDialectInterface : public OpFolderDialectInterface { 55 using OpFolderDialectInterface::OpFolderDialectInterface; 56 57 /// Registered hook to check if the given region, which is attached to an 58 /// operation that is *not* isolated from above, should be used when 59 /// materializing constants. 60 bool shouldMaterializeInto(Region *region) const final { 61 // If this is a one region operation, then insert into it. 62 return isa<OneRegionOp>(region->getParentOp()); 63 } 64 }; 65 66 /// This class defines the interface for handling inlining with standard 67 /// operations. 68 struct TestInlinerInterface : public DialectInlinerInterface { 69 using DialectInlinerInterface::DialectInlinerInterface; 70 71 //===--------------------------------------------------------------------===// 72 // Analysis Hooks 73 //===--------------------------------------------------------------------===// 74 75 bool isLegalToInline(Region *, Region *, BlockAndValueMapping &) const final { 76 // Inlining into test dialect regions is legal. 77 return true; 78 } 79 bool isLegalToInline(Operation *, Region *, 80 BlockAndValueMapping &) const final { 81 return true; 82 } 83 84 bool shouldAnalyzeRecursively(Operation *op) const final { 85 // Analyze recursively if this is not a functional region operation, it 86 // froms a separate functional scope. 87 return !isa<FunctionalRegionOp>(op); 88 } 89 90 //===--------------------------------------------------------------------===// 91 // Transformation Hooks 92 //===--------------------------------------------------------------------===// 93 94 /// Handle the given inlined terminator by replacing it with a new operation 95 /// as necessary. 96 void handleTerminator(Operation *op, 97 ArrayRef<Value> valuesToRepl) const final { 98 // Only handle "test.return" here. 99 auto returnOp = dyn_cast<TestReturnOp>(op); 100 if (!returnOp) 101 return; 102 103 // Replace the values directly with the return operands. 104 assert(returnOp.getNumOperands() == valuesToRepl.size()); 105 for (const auto &it : llvm::enumerate(returnOp.getOperands())) 106 valuesToRepl[it.index()].replaceAllUsesWith(it.value()); 107 } 108 109 /// Attempt to materialize a conversion for a type mismatch between a call 110 /// from this dialect, and a callable region. This method should generate an 111 /// operation that takes 'input' as the only operand, and produces a single 112 /// result of 'resultType'. If a conversion can not be generated, nullptr 113 /// should be returned. 114 Operation *materializeCallConversion(OpBuilder &builder, Value input, 115 Type resultType, 116 Location conversionLoc) const final { 117 // Only allow conversion for i16/i32 types. 118 if (!(resultType.isSignlessInteger(16) || 119 resultType.isSignlessInteger(32)) || 120 !(input.getType().isSignlessInteger(16) || 121 input.getType().isSignlessInteger(32))) 122 return nullptr; 123 return builder.create<TestCastOp>(conversionLoc, resultType, input); 124 } 125 }; 126 } // end anonymous namespace 127 128 //===----------------------------------------------------------------------===// 129 // TestDialect 130 //===----------------------------------------------------------------------===// 131 132 TestDialect::TestDialect(MLIRContext *context) 133 : Dialect(getDialectNamespace(), context) { 134 addOperations< 135 #define GET_OP_LIST 136 #include "TestOps.cpp.inc" 137 >(); 138 addInterfaces<TestOpAsmInterface, TestOpFolderDialectInterface, 139 TestInlinerInterface>(); 140 addTypes<TestType>(); 141 allowUnknownOperations(); 142 } 143 144 Type TestDialect::parseType(DialectAsmParser &parser) const { 145 if (failed(parser.parseKeyword("test_type"))) 146 return Type(); 147 return TestType::get(getContext()); 148 } 149 150 void TestDialect::printType(Type type, DialectAsmPrinter &printer) const { 151 assert(type.isa<TestType>() && "unexpected type"); 152 printer << "test_type"; 153 } 154 155 LogicalResult TestDialect::verifyOperationAttribute(Operation *op, 156 NamedAttribute namedAttr) { 157 if (namedAttr.first == "test.invalid_attr") 158 return op->emitError() << "invalid to use 'test.invalid_attr'"; 159 return success(); 160 } 161 162 LogicalResult TestDialect::verifyRegionArgAttribute(Operation *op, 163 unsigned regionIndex, 164 unsigned argIndex, 165 NamedAttribute namedAttr) { 166 if (namedAttr.first == "test.invalid_attr") 167 return op->emitError() << "invalid to use 'test.invalid_attr'"; 168 return success(); 169 } 170 171 LogicalResult 172 TestDialect::verifyRegionResultAttribute(Operation *op, unsigned regionIndex, 173 unsigned resultIndex, 174 NamedAttribute namedAttr) { 175 if (namedAttr.first == "test.invalid_attr") 176 return op->emitError() << "invalid to use 'test.invalid_attr'"; 177 return success(); 178 } 179 180 //===----------------------------------------------------------------------===// 181 // TestBranchOp 182 //===----------------------------------------------------------------------===// 183 184 Optional<MutableOperandRange> 185 TestBranchOp::getMutableSuccessorOperands(unsigned index) { 186 assert(index == 0 && "invalid successor index"); 187 return targetOperandsMutable(); 188 } 189 190 //===----------------------------------------------------------------------===// 191 // TestFoldToCallOp 192 //===----------------------------------------------------------------------===// 193 194 namespace { 195 struct FoldToCallOpPattern : public OpRewritePattern<FoldToCallOp> { 196 using OpRewritePattern<FoldToCallOp>::OpRewritePattern; 197 198 LogicalResult matchAndRewrite(FoldToCallOp op, 199 PatternRewriter &rewriter) const override { 200 rewriter.replaceOpWithNewOp<CallOp>(op, ArrayRef<Type>(), op.calleeAttr(), 201 ValueRange()); 202 return success(); 203 } 204 }; 205 } // end anonymous namespace 206 207 void FoldToCallOp::getCanonicalizationPatterns( 208 OwningRewritePatternList &results, MLIRContext *context) { 209 results.insert<FoldToCallOpPattern>(context); 210 } 211 212 //===----------------------------------------------------------------------===// 213 // Test IsolatedRegionOp - parse passthrough region arguments. 214 //===----------------------------------------------------------------------===// 215 216 static ParseResult parseIsolatedRegionOp(OpAsmParser &parser, 217 OperationState &result) { 218 OpAsmParser::OperandType argInfo; 219 Type argType = parser.getBuilder().getIndexType(); 220 221 // Parse the input operand. 222 if (parser.parseOperand(argInfo) || 223 parser.resolveOperand(argInfo, argType, result.operands)) 224 return failure(); 225 226 // Parse the body region, and reuse the operand info as the argument info. 227 Region *body = result.addRegion(); 228 return parser.parseRegion(*body, argInfo, argType, 229 /*enableNameShadowing=*/true); 230 } 231 232 static void print(OpAsmPrinter &p, IsolatedRegionOp op) { 233 p << "test.isolated_region "; 234 p.printOperand(op.getOperand()); 235 p.shadowRegionArgs(op.region(), op.getOperand()); 236 p.printRegion(op.region(), /*printEntryBlockArgs=*/false); 237 } 238 239 //===----------------------------------------------------------------------===// 240 // Test SSACFGRegionOp 241 //===----------------------------------------------------------------------===// 242 243 RegionKind SSACFGRegionOp::getRegionKind(unsigned index) { 244 return RegionKind::SSACFG; 245 } 246 247 //===----------------------------------------------------------------------===// 248 // Test GraphRegionOp 249 //===----------------------------------------------------------------------===// 250 251 static ParseResult parseGraphRegionOp(OpAsmParser &parser, 252 OperationState &result) { 253 // Parse the body region, and reuse the operand info as the argument info. 254 Region *body = result.addRegion(); 255 return parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{}); 256 } 257 258 static void print(OpAsmPrinter &p, GraphRegionOp op) { 259 p << "test.graph_region "; 260 p.printRegion(op.region(), /*printEntryBlockArgs=*/false); 261 } 262 263 RegionKind GraphRegionOp::getRegionKind(unsigned index) { 264 return RegionKind::Graph; 265 } 266 267 //===----------------------------------------------------------------------===// 268 // Test AffineScopeOp 269 //===----------------------------------------------------------------------===// 270 271 static ParseResult parseAffineScopeOp(OpAsmParser &parser, 272 OperationState &result) { 273 // Parse the body region, and reuse the operand info as the argument info. 274 Region *body = result.addRegion(); 275 return parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{}); 276 } 277 278 static void print(OpAsmPrinter &p, AffineScopeOp op) { 279 p << "test.affine_scope "; 280 p.printRegion(op.region(), /*printEntryBlockArgs=*/false); 281 } 282 283 //===----------------------------------------------------------------------===// 284 // Test parser. 285 //===----------------------------------------------------------------------===// 286 287 static ParseResult parseWrappedKeywordOp(OpAsmParser &parser, 288 OperationState &result) { 289 StringRef keyword; 290 if (parser.parseKeyword(&keyword)) 291 return failure(); 292 result.addAttribute("keyword", parser.getBuilder().getStringAttr(keyword)); 293 return success(); 294 } 295 296 static void print(OpAsmPrinter &p, WrappedKeywordOp op) { 297 p << WrappedKeywordOp::getOperationName() << " " << op.keyword(); 298 } 299 300 //===----------------------------------------------------------------------===// 301 // Test WrapRegionOp - wrapping op exercising `parseGenericOperation()`. 302 303 static ParseResult parseWrappingRegionOp(OpAsmParser &parser, 304 OperationState &result) { 305 if (parser.parseKeyword("wraps")) 306 return failure(); 307 308 // Parse the wrapped op in a region 309 Region &body = *result.addRegion(); 310 body.push_back(new Block); 311 Block &block = body.back(); 312 Operation *wrapped_op = parser.parseGenericOperation(&block, block.begin()); 313 if (!wrapped_op) 314 return failure(); 315 316 // Create a return terminator in the inner region, pass as operand to the 317 // terminator the returned values from the wrapped operation. 318 SmallVector<Value, 8> return_operands(wrapped_op->getResults()); 319 OpBuilder builder(parser.getBuilder().getContext()); 320 builder.setInsertionPointToEnd(&block); 321 builder.create<TestReturnOp>(wrapped_op->getLoc(), return_operands); 322 323 // Get the results type for the wrapping op from the terminator operands. 324 Operation &return_op = body.back().back(); 325 result.types.append(return_op.operand_type_begin(), 326 return_op.operand_type_end()); 327 328 // Use the location of the wrapped op for the "test.wrapping_region" op. 329 result.location = wrapped_op->getLoc(); 330 331 return success(); 332 } 333 334 static void print(OpAsmPrinter &p, WrappingRegionOp op) { 335 p << op.getOperationName() << " wraps "; 336 p.printGenericOp(&op.region().front().front()); 337 } 338 339 //===----------------------------------------------------------------------===// 340 // Test PolyForOp - parse list of region arguments. 341 //===----------------------------------------------------------------------===// 342 343 static ParseResult parsePolyForOp(OpAsmParser &parser, OperationState &result) { 344 SmallVector<OpAsmParser::OperandType, 4> ivsInfo; 345 // Parse list of region arguments without a delimiter. 346 if (parser.parseRegionArgumentList(ivsInfo)) 347 return failure(); 348 349 // Parse the body region. 350 Region *body = result.addRegion(); 351 auto &builder = parser.getBuilder(); 352 SmallVector<Type, 4> argTypes(ivsInfo.size(), builder.getIndexType()); 353 return parser.parseRegion(*body, ivsInfo, argTypes); 354 } 355 356 //===----------------------------------------------------------------------===// 357 // Test removing op with inner ops. 358 //===----------------------------------------------------------------------===// 359 360 namespace { 361 struct TestRemoveOpWithInnerOps 362 : public OpRewritePattern<TestOpWithRegionPattern> { 363 using OpRewritePattern<TestOpWithRegionPattern>::OpRewritePattern; 364 365 LogicalResult matchAndRewrite(TestOpWithRegionPattern op, 366 PatternRewriter &rewriter) const override { 367 rewriter.eraseOp(op); 368 return success(); 369 } 370 }; 371 } // end anonymous namespace 372 373 void TestOpWithRegionPattern::getCanonicalizationPatterns( 374 OwningRewritePatternList &results, MLIRContext *context) { 375 results.insert<TestRemoveOpWithInnerOps>(context); 376 } 377 378 OpFoldResult TestOpWithRegionFold::fold(ArrayRef<Attribute> operands) { 379 return operand(); 380 } 381 382 LogicalResult TestOpWithVariadicResultsAndFolder::fold( 383 ArrayRef<Attribute> operands, SmallVectorImpl<OpFoldResult> &results) { 384 for (Value input : this->operands()) { 385 results.push_back(input); 386 } 387 return success(); 388 } 389 390 OpFoldResult TestOpInPlaceFold::fold(ArrayRef<Attribute> operands) { 391 assert(operands.size() == 1); 392 if (operands.front()) { 393 setAttr("attr", operands.front()); 394 return getResult(); 395 } 396 return {}; 397 } 398 399 LogicalResult OpWithInferTypeInterfaceOp::inferReturnTypes( 400 MLIRContext *, Optional<Location> location, ValueRange operands, 401 DictionaryAttr attributes, RegionRange regions, 402 SmallVectorImpl<Type> &inferredReturnTypes) { 403 if (operands[0].getType() != operands[1].getType()) { 404 return emitOptionalError(location, "operand type mismatch ", 405 operands[0].getType(), " vs ", 406 operands[1].getType()); 407 } 408 inferredReturnTypes.assign({operands[0].getType()}); 409 return success(); 410 } 411 412 LogicalResult OpWithShapedTypeInferTypeInterfaceOp::inferReturnTypeComponents( 413 MLIRContext *context, Optional<Location> location, ValueRange operands, 414 DictionaryAttr attributes, RegionRange regions, 415 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { 416 // Create return type consisting of the last element of the first operand. 417 auto operandType = *operands.getTypes().begin(); 418 auto sval = operandType.dyn_cast<ShapedType>(); 419 if (!sval) { 420 return emitOptionalError(location, "only shaped type operands allowed"); 421 } 422 int64_t dim = 423 sval.hasRank() ? sval.getShape().front() : ShapedType::kDynamicSize; 424 auto type = IntegerType::get(17, context); 425 inferredReturnShapes.push_back(ShapedTypeComponents({dim}, type)); 426 return success(); 427 } 428 429 LogicalResult OpWithShapedTypeInferTypeInterfaceOp::reifyReturnTypeShapes( 430 OpBuilder &builder, llvm::SmallVectorImpl<Value> &shapes) { 431 shapes = SmallVector<Value, 1>{ 432 builder.createOrFold<DimOp>(getLoc(), getOperand(0), 0)}; 433 return success(); 434 } 435 436 //===----------------------------------------------------------------------===// 437 // Test SideEffect interfaces 438 //===----------------------------------------------------------------------===// 439 440 namespace { 441 /// A test resource for side effects. 442 struct TestResource : public SideEffects::Resource::Base<TestResource> { 443 StringRef getName() final { return "<Test>"; } 444 }; 445 } // end anonymous namespace 446 447 void SideEffectOp::getEffects( 448 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) { 449 // Check for an effects attribute on the op instance. 450 ArrayAttr effectsAttr = getAttrOfType<ArrayAttr>("effects"); 451 if (!effectsAttr) 452 return; 453 454 // If there is one, it is an array of dictionary attributes that hold 455 // information on the effects of this operation. 456 for (Attribute element : effectsAttr) { 457 DictionaryAttr effectElement = element.cast<DictionaryAttr>(); 458 459 // Get the specific memory effect. 460 MemoryEffects::Effect *effect = 461 llvm::StringSwitch<MemoryEffects::Effect *>( 462 effectElement.get("effect").cast<StringAttr>().getValue()) 463 .Case("allocate", MemoryEffects::Allocate::get()) 464 .Case("free", MemoryEffects::Free::get()) 465 .Case("read", MemoryEffects::Read::get()) 466 .Case("write", MemoryEffects::Write::get()); 467 468 // Check for a result to affect. 469 Value value; 470 if (effectElement.get("on_result")) 471 value = getResult(); 472 473 // Check for a non-default resource to use. 474 SideEffects::Resource *resource = SideEffects::DefaultResource::get(); 475 if (effectElement.get("test_resource")) 476 resource = TestResource::get(); 477 478 effects.emplace_back(effect, value, resource); 479 } 480 } 481 482 //===----------------------------------------------------------------------===// 483 // StringAttrPrettyNameOp 484 //===----------------------------------------------------------------------===// 485 486 // This op has fancy handling of its SSA result name. 487 static ParseResult parseStringAttrPrettyNameOp(OpAsmParser &parser, 488 OperationState &result) { 489 // Add the result types. 490 for (size_t i = 0, e = parser.getNumResults(); i != e; ++i) 491 result.addTypes(parser.getBuilder().getIntegerType(32)); 492 493 if (parser.parseOptionalAttrDictWithKeyword(result.attributes)) 494 return failure(); 495 496 // If the attribute dictionary contains no 'names' attribute, infer it from 497 // the SSA name (if specified). 498 bool hadNames = llvm::any_of(result.attributes, [](NamedAttribute attr) { 499 return attr.first == "names"; 500 }); 501 502 // If there was no name specified, check to see if there was a useful name 503 // specified in the asm file. 504 if (hadNames || parser.getNumResults() == 0) 505 return success(); 506 507 SmallVector<StringRef, 4> names; 508 auto *context = result.getContext(); 509 510 for (size_t i = 0, e = parser.getNumResults(); i != e; ++i) { 511 auto resultName = parser.getResultName(i); 512 StringRef nameStr; 513 if (!resultName.first.empty() && !isdigit(resultName.first[0])) 514 nameStr = resultName.first; 515 516 names.push_back(nameStr); 517 } 518 519 auto namesAttr = parser.getBuilder().getStrArrayAttr(names); 520 result.attributes.push_back({Identifier::get("names", context), namesAttr}); 521 return success(); 522 } 523 524 static void print(OpAsmPrinter &p, StringAttrPrettyNameOp op) { 525 p << "test.string_attr_pretty_name"; 526 527 // Note that we only need to print the "name" attribute if the asmprinter 528 // result name disagrees with it. This can happen in strange cases, e.g. 529 // when there are conflicts. 530 bool namesDisagree = op.names().size() != op.getNumResults(); 531 532 SmallString<32> resultNameStr; 533 for (size_t i = 0, e = op.getNumResults(); i != e && !namesDisagree; ++i) { 534 resultNameStr.clear(); 535 llvm::raw_svector_ostream tmpStream(resultNameStr); 536 p.printOperand(op.getResult(i), tmpStream); 537 538 auto expectedName = op.names()[i].dyn_cast<StringAttr>(); 539 if (!expectedName || 540 tmpStream.str().drop_front() != expectedName.getValue()) { 541 namesDisagree = true; 542 } 543 } 544 545 if (namesDisagree) 546 p.printOptionalAttrDictWithKeyword(op.getAttrs()); 547 else 548 p.printOptionalAttrDictWithKeyword(op.getAttrs(), {"names"}); 549 } 550 551 // We set the SSA name in the asm syntax to the contents of the name 552 // attribute. 553 void StringAttrPrettyNameOp::getAsmResultNames( 554 function_ref<void(Value, StringRef)> setNameFn) { 555 556 auto value = names(); 557 for (size_t i = 0, e = value.size(); i != e; ++i) 558 if (auto str = value[i].dyn_cast<StringAttr>()) 559 if (!str.getValue().empty()) 560 setNameFn(getResult(i), str.getValue()); 561 } 562 563 //===----------------------------------------------------------------------===// 564 // RegionIfOp 565 //===----------------------------------------------------------------------===// 566 567 static void print(OpAsmPrinter &p, RegionIfOp op) { 568 p << RegionIfOp::getOperationName() << " "; 569 p.printOperands(op.getOperands()); 570 p << ": " << op.getOperandTypes(); 571 p.printArrowTypeList(op.getResultTypes()); 572 p << " then"; 573 p.printRegion(op.thenRegion(), 574 /*printEntryBlockArgs=*/true, 575 /*printBlockTerminators=*/true); 576 p << " else"; 577 p.printRegion(op.elseRegion(), 578 /*printEntryBlockArgs=*/true, 579 /*printBlockTerminators=*/true); 580 p << " join"; 581 p.printRegion(op.joinRegion(), 582 /*printEntryBlockArgs=*/true, 583 /*printBlockTerminators=*/true); 584 } 585 586 static ParseResult parseRegionIfOp(OpAsmParser &parser, 587 OperationState &result) { 588 SmallVector<OpAsmParser::OperandType, 2> operandInfos; 589 SmallVector<Type, 2> operandTypes; 590 591 result.regions.reserve(3); 592 Region *thenRegion = result.addRegion(); 593 Region *elseRegion = result.addRegion(); 594 Region *joinRegion = result.addRegion(); 595 596 // Parse operand, type and arrow type lists. 597 if (parser.parseOperandList(operandInfos) || 598 parser.parseColonTypeList(operandTypes) || 599 parser.parseArrowTypeList(result.types)) 600 return failure(); 601 602 // Parse all attached regions. 603 if (parser.parseKeyword("then") || parser.parseRegion(*thenRegion, {}, {}) || 604 parser.parseKeyword("else") || parser.parseRegion(*elseRegion, {}, {}) || 605 parser.parseKeyword("join") || parser.parseRegion(*joinRegion, {}, {})) 606 return failure(); 607 608 return parser.resolveOperands(operandInfos, operandTypes, 609 parser.getCurrentLocation(), result.operands); 610 } 611 612 OperandRange RegionIfOp::getSuccessorEntryOperands(unsigned index) { 613 assert(index < 2 && "invalid region index"); 614 return getOperands(); 615 } 616 617 void RegionIfOp::getSuccessorRegions( 618 Optional<unsigned> index, ArrayRef<Attribute> operands, 619 SmallVectorImpl<RegionSuccessor> ®ions) { 620 // We always branch to the join region. 621 if (index.hasValue()) { 622 if (index.getValue() < 2) 623 regions.push_back(RegionSuccessor(&joinRegion(), getJoinArgs())); 624 else 625 regions.push_back(RegionSuccessor(getResults())); 626 return; 627 } 628 629 // The then and else regions are the entry regions of this op. 630 regions.push_back(RegionSuccessor(&thenRegion(), getThenArgs())); 631 regions.push_back(RegionSuccessor(&elseRegion(), getElseArgs())); 632 } 633 634 //===----------------------------------------------------------------------===// 635 // Dialect Registration 636 //===----------------------------------------------------------------------===// 637 638 // Static initialization for Test dialect registration. 639 static DialectRegistration<TestDialect> testDialect; 640 641 #include "TestOpEnums.cpp.inc" 642 #include "TestOpStructs.cpp.inc" 643 #include "TestTypeInterfaces.cpp.inc" 644 645 #define GET_OP_CLASSES 646 #include "TestOps.cpp.inc" 647