1//===- FunctionInterfaces.td - Function interfaces --------*- tablegen -*-===// 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// This file contains definitions for interfaces that support the definition of 10// "function-like" operations. 11// 12//===----------------------------------------------------------------------===// 13 14#ifndef MLIR_IR_FUNCTIONINTERFACES_TD_ 15#define MLIR_IR_FUNCTIONINTERFACES_TD_ 16 17include "mlir/IR/OpBase.td" 18 19//===----------------------------------------------------------------------===// 20// FunctionOpInterface 21//===----------------------------------------------------------------------===// 22 23def FunctionOpInterface : OpInterface<"FunctionOpInterface"> { 24 let cppNamespace = "::mlir"; 25 let description = [{ 26 This interfaces provides support for interacting with operations that 27 behave like functions. In particular, these operations: 28 29 - must be symbols, i.e. have the `Symbol` trait. 30 - must have a single region, that may be comprised with multiple blocks, 31 that corresponds to the function body. 32 * when this region is empty, the operation corresponds to an external 33 function. 34 * leading arguments of the first block of the region are treated as 35 function arguments. 36 37 The function, aside from implementing the various interface methods, 38 should have the following ODS arguments: 39 40 - `function_type` (required) 41 * A TypeAttr that holds the signature type of the function. 42 43 - `arg_attrs` (optional) 44 * An ArrayAttr of DictionaryAttr that contains attribute dictionaries 45 for each of the function arguments. 46 47 - `res_attrs` (optional) 48 * An ArrayAttr of DictionaryAttr that contains attribute dictionaries 49 for each of the function results. 50 }]; 51 let methods = [ 52 InterfaceMethod<[{ 53 Returns the function argument types based exclusively on 54 the type (to allow for this method may be called on function 55 declarations). 56 }], 57 "::llvm::ArrayRef<::mlir::Type>", "getArgumentTypes">, 58 InterfaceMethod<[{ 59 Returns the function result types based exclusively on 60 the type (to allow for this method may be called on function 61 declarations). 62 }], 63 "::llvm::ArrayRef<::mlir::Type>", "getResultTypes">, 64 InterfaceMethod<[{ 65 Returns a clone of the function type with the given argument and 66 result types. 67 68 Note: The default implementation assumes the function type has 69 an appropriate clone method: 70 `Type clone(ArrayRef<Type> inputs, ArrayRef<Type> results)` 71 }], 72 "::mlir::Type", "cloneTypeWith", (ins 73 "::mlir::TypeRange":$inputs, "::mlir::TypeRange":$results 74 ), /*methodBody=*/[{}], /*defaultImplementation=*/[{ 75 return $_op.getFunctionType().clone(inputs, results); 76 }]>, 77 78 InterfaceMethod<[{ 79 Verify the contents of the body of this function. 80 81 Note: The default implementation merely checks that if the entry block 82 exists, it has the same number and type of arguments as the function type. 83 }], 84 "::mlir::LogicalResult", "verifyBody", (ins), 85 /*methodBody=*/[{}], /*defaultImplementation=*/[{ 86 if ($_op.isExternal()) 87 return success(); 88 ArrayRef<Type> fnInputTypes = $_op.getArgumentTypes(); 89 // NOTE: This should just be $_op.front() but access generically 90 // because the interface methods defined here may be shadowed in 91 // arbitrary ways. https://github.com/llvm/llvm-project/issues/54807 92 Block &entryBlock = $_op->getRegion(0).front(); 93 94 unsigned numArguments = fnInputTypes.size(); 95 if (entryBlock.getNumArguments() != numArguments) 96 return $_op.emitOpError("entry block must have ") 97 << numArguments << " arguments to match function signature"; 98 99 for (unsigned i = 0, e = fnInputTypes.size(); i != e; ++i) { 100 Type argType = entryBlock.getArgument(i).getType(); 101 if (fnInputTypes[i] != argType) { 102 return $_op.emitOpError("type of entry block argument #") 103 << i << '(' << argType 104 << ") must match the type of the corresponding argument in " 105 << "function signature(" << fnInputTypes[i] << ')'; 106 } 107 } 108 109 return success(); 110 }]>, 111 InterfaceMethod<[{ 112 Verify the type attribute of the function for derived op-specific 113 invariants. 114 }], 115 "::mlir::LogicalResult", "verifyType", (ins), 116 /*methodBody=*/[{}], /*defaultImplementation=*/[{ 117 return success(); 118 }]>, 119 ]; 120 121 let extraClassDeclaration = [{ 122 //===------------------------------------------------------------------===// 123 // Name 124 //===------------------------------------------------------------------===// 125 126 /// Return the name of the function. 127 StringRef getName() { return SymbolTable::getSymbolName(*this); } 128 }]; 129 let extraTraitClassDeclaration = [{ 130 //===------------------------------------------------------------------===// 131 // Builders 132 //===------------------------------------------------------------------===// 133 134 /// Build the function with the given name, attributes, and type. This 135 /// builder also inserts an entry block into the function body with the 136 /// given argument types. 137 static void buildWithEntryBlock( 138 OpBuilder &builder, OperationState &state, StringRef name, Type type, 139 ArrayRef<NamedAttribute> attrs, ArrayRef<Type> inputTypes) { 140 state.addAttribute(SymbolTable::getSymbolAttrName(), 141 builder.getStringAttr(name)); 142 state.addAttribute(function_interface_impl::getTypeAttrName(), 143 TypeAttr::get(type)); 144 state.attributes.append(attrs.begin(), attrs.end()); 145 146 // Add the function body. 147 Region *bodyRegion = state.addRegion(); 148 Block *body = new Block(); 149 bodyRegion->push_back(body); 150 for (Type input : inputTypes) 151 body->addArgument(input, state.location); 152 } 153 }]; 154 let extraSharedClassDeclaration = [{ 155 /// Block list iterator types. 156 using BlockListType = Region::BlockListType; 157 using iterator = BlockListType::iterator; 158 using reverse_iterator = BlockListType::reverse_iterator; 159 160 /// Block argument iterator types. 161 using BlockArgListType = Region::BlockArgListType; 162 using args_iterator = BlockArgListType::iterator; 163 164 //===------------------------------------------------------------------===// 165 // Body Handling 166 //===------------------------------------------------------------------===// 167 168 /// Returns true if this function is external, i.e. it has no body. 169 bool isExternal() { return empty(); } 170 171 /// Return the region containing the body of this function. 172 Region &getBody() { return $_op->getRegion(0); } 173 174 /// Delete all blocks from this function. 175 void eraseBody() { 176 getBody().dropAllReferences(); 177 getBody().getBlocks().clear(); 178 } 179 180 /// Return the list of blocks within the function body. 181 BlockListType &getBlocks() { return getBody().getBlocks(); } 182 183 iterator begin() { return getBody().begin(); } 184 iterator end() { return getBody().end(); } 185 reverse_iterator rbegin() { return getBody().rbegin(); } 186 reverse_iterator rend() { return getBody().rend(); } 187 188 /// Returns true if this function has no blocks within the body. 189 bool empty() { return getBody().empty(); } 190 191 /// Push a new block to the back of the body region. 192 void push_back(Block *block) { getBody().push_back(block); } 193 194 /// Push a new block to the front of the body region. 195 void push_front(Block *block) { getBody().push_front(block); } 196 197 /// Return the last block in the body region. 198 Block &back() { return getBody().back(); } 199 200 /// Return the first block in the body region. 201 Block &front() { return getBody().front(); } 202 203 /// Add an entry block to an empty function, and set up the block arguments 204 /// to match the signature of the function. The newly inserted entry block 205 /// is returned. 206 Block *addEntryBlock() { 207 assert(empty() && "function already has an entry block"); 208 Block *entry = new Block(); 209 push_back(entry); 210 211 // FIXME: Allow for passing in locations for these arguments instead of using 212 // the operations location. 213 ArrayRef<Type> inputTypes = $_op.getArgumentTypes(); 214 SmallVector<Location> locations(inputTypes.size(), 215 $_op.getOperation()->getLoc()); 216 entry->addArguments(inputTypes, locations); 217 return entry; 218 } 219 220 /// Add a normal block to the end of the function's block list. The function 221 /// should at least already have an entry block. 222 Block *addBlock() { 223 assert(!empty() && "function should at least have an entry block"); 224 push_back(new Block()); 225 return &back(); 226 } 227 228 //===------------------------------------------------------------------===// 229 // Type Attribute Handling 230 //===------------------------------------------------------------------===// 231 232 /// Change the type of this function in place. This is an extremely dangerous 233 /// operation and it is up to the caller to ensure that this is legal for 234 /// this function, and to restore invariants: 235 /// - the entry block args must be updated to match the function params. 236 /// - the argument/result attributes may need an update: if the new type 237 /// has less parameters we drop the extra attributes, if there are more 238 /// parameters they won't have any attributes. 239 void setType(Type newType) { 240 function_interface_impl::setFunctionType(this->getOperation(), newType); 241 } 242 243 // FIXME: These functions should be removed in favor of just forwarding to 244 // the derived operation, which should already have these defined 245 // (via ODS). 246 247 /// Returns the name of the attribute used for function types. 248 static StringRef getTypeAttrName() { 249 return function_interface_impl::getTypeAttrName(); 250 } 251 252 /// Returns the name of the attribute used for function argument attributes. 253 static StringRef getArgDictAttrName() { 254 return function_interface_impl::getArgDictAttrName(); 255 } 256 257 /// Returns the name of the attribute used for function argument attributes. 258 static StringRef getResultDictAttrName() { 259 return function_interface_impl::getResultDictAttrName(); 260 } 261 262 /// Return the attribute containing the type of this function. 263 TypeAttr getFunctionTypeAttr() { 264 return this->getOperation()->template getAttrOfType<TypeAttr>( 265 getTypeAttrName()); 266 } 267 268 /// Return the type of this function. 269 Type getFunctionType() { return getFunctionTypeAttr().getValue(); } 270 271 //===------------------------------------------------------------------===// 272 // Argument and Result Handling 273 //===------------------------------------------------------------------===// 274 275 /// Returns the number of function arguments. 276 unsigned getNumArguments() { return $_op.getArgumentTypes().size(); } 277 278 /// Returns the number of function results. 279 unsigned getNumResults() { return $_op.getResultTypes().size(); } 280 281 /// Returns the entry block function argument at the given index. 282 BlockArgument getArgument(unsigned idx) { 283 return getBody().getArgument(idx); 284 } 285 286 /// Support argument iteration. 287 args_iterator args_begin() { return getBody().args_begin(); } 288 args_iterator args_end() { return getBody().args_end(); } 289 BlockArgListType getArguments() { return getBody().getArguments(); } 290 291 /// Insert a single argument of type `argType` with attributes `argAttrs` and 292 /// location `argLoc` at `argIndex`. 293 void insertArgument(unsigned argIndex, Type argType, DictionaryAttr argAttrs, 294 Location argLoc) { 295 insertArguments({argIndex}, {argType}, {argAttrs}, {argLoc}); 296 } 297 298 /// Inserts arguments with the listed types, attributes, and locations at the 299 /// listed indices. `argIndices` must be sorted. Arguments are inserted in the 300 /// order they are listed, such that arguments with identical index will 301 /// appear in the same order that they were listed here. 302 void insertArguments(ArrayRef<unsigned> argIndices, TypeRange argTypes, 303 ArrayRef<DictionaryAttr> argAttrs, 304 ArrayRef<Location> argLocs) { 305 unsigned originalNumArgs = $_op.getNumArguments(); 306 Type newType = $_op.getTypeWithArgsAndResults( 307 argIndices, argTypes, /*resultIndices=*/{}, /*resultTypes=*/{}); 308 function_interface_impl::insertFunctionArguments( 309 this->getOperation(), argIndices, argTypes, argAttrs, argLocs, 310 originalNumArgs, newType); 311 } 312 313 /// Insert a single result of type `resultType` at `resultIndex`. 314 void insertResult(unsigned resultIndex, Type resultType, 315 DictionaryAttr resultAttrs) { 316 insertResults({resultIndex}, {resultType}, {resultAttrs}); 317 } 318 319 /// Inserts results with the listed types at the listed indices. 320 /// `resultIndices` must be sorted. Results are inserted in the order they are 321 /// listed, such that results with identical index will appear in the same 322 /// order that they were listed here. 323 void insertResults(ArrayRef<unsigned> resultIndices, TypeRange resultTypes, 324 ArrayRef<DictionaryAttr> resultAttrs) { 325 unsigned originalNumResults = $_op.getNumResults(); 326 Type newType = $_op.getTypeWithArgsAndResults( 327 /*argIndices=*/{}, /*argTypes=*/{}, resultIndices, resultTypes); 328 function_interface_impl::insertFunctionResults( 329 this->getOperation(), resultIndices, resultTypes, resultAttrs, 330 originalNumResults, newType); 331 } 332 333 /// Erase a single argument at `argIndex`. 334 void eraseArgument(unsigned argIndex) { 335 BitVector argsToErase($_op.getNumArguments()); 336 argsToErase.set(argIndex); 337 eraseArguments(argsToErase); 338 } 339 340 /// Erases the arguments listed in `argIndices`. 341 void eraseArguments(const BitVector &argIndices) { 342 Type newType = $_op.getTypeWithoutArgs(argIndices); 343 function_interface_impl::eraseFunctionArguments( 344 this->getOperation(), argIndices, newType); 345 } 346 347 /// Erase a single result at `resultIndex`. 348 void eraseResult(unsigned resultIndex) { 349 BitVector resultsToErase($_op.getNumResults()); 350 resultsToErase.set(resultIndex); 351 eraseResults(resultsToErase); 352 } 353 354 /// Erases the results listed in `resultIndices`. 355 void eraseResults(const BitVector &resultIndices) { 356 Type newType = $_op.getTypeWithoutResults(resultIndices); 357 function_interface_impl::eraseFunctionResults( 358 this->getOperation(), resultIndices, newType); 359 } 360 361 /// Return the type of this function with the specified arguments and 362 /// results inserted. This is used to update the function's signature in 363 /// the `insertArguments` and `insertResults` methods. The arrays must be 364 /// sorted by increasing index. 365 Type getTypeWithArgsAndResults( 366 ArrayRef<unsigned> argIndices, TypeRange argTypes, 367 ArrayRef<unsigned> resultIndices, TypeRange resultTypes) { 368 SmallVector<Type> argStorage, resultStorage; 369 TypeRange newArgTypes = function_interface_impl::insertTypesInto( 370 $_op.getArgumentTypes(), argIndices, argTypes, argStorage); 371 TypeRange newResultTypes = function_interface_impl::insertTypesInto( 372 $_op.getResultTypes(), resultIndices, resultTypes, resultStorage); 373 return $_op.cloneTypeWith(newArgTypes, newResultTypes); 374 } 375 376 /// Return the type of this function without the specified arguments and 377 /// results. This is used to update the function's signature in the 378 /// `eraseArguments` and `eraseResults` methods. 379 Type getTypeWithoutArgsAndResults( 380 const BitVector &argIndices, const BitVector &resultIndices) { 381 SmallVector<Type> argStorage, resultStorage; 382 TypeRange newArgTypes = function_interface_impl::filterTypesOut( 383 $_op.getArgumentTypes(), argIndices, argStorage); 384 TypeRange newResultTypes = function_interface_impl::filterTypesOut( 385 $_op.getResultTypes(), resultIndices, resultStorage); 386 return $_op.cloneTypeWith(newArgTypes, newResultTypes); 387 } 388 Type getTypeWithoutArgs(const BitVector &argIndices) { 389 SmallVector<Type> argStorage; 390 TypeRange newArgTypes = function_interface_impl::filterTypesOut( 391 $_op.getArgumentTypes(), argIndices, argStorage); 392 return $_op.cloneTypeWith(newArgTypes, $_op.getResultTypes()); 393 } 394 Type getTypeWithoutResults(const BitVector &resultIndices) { 395 SmallVector<Type> resultStorage; 396 TypeRange newResultTypes = function_interface_impl::filterTypesOut( 397 $_op.getResultTypes(), resultIndices, resultStorage); 398 return $_op.cloneTypeWith($_op.getArgumentTypes(), newResultTypes); 399 } 400 401 //===------------------------------------------------------------------===// 402 // Argument Attributes 403 //===------------------------------------------------------------------===// 404 405 /// Return all of the attributes for the argument at 'index'. 406 ArrayRef<NamedAttribute> getArgAttrs(unsigned index) { 407 return function_interface_impl::getArgAttrs(this->getOperation(), index); 408 } 409 410 /// Return an ArrayAttr containing all argument attribute dictionaries of 411 /// this function, or nullptr if no arguments have attributes. 412 ArrayAttr getAllArgAttrs() { 413 return this->getOperation()->template getAttrOfType<ArrayAttr>( 414 getArgDictAttrName()); 415 } 416 /// Return all argument attributes of this function. 417 void getAllArgAttrs(SmallVectorImpl<DictionaryAttr> &result) { 418 if (ArrayAttr argAttrs = getAllArgAttrs()) { 419 auto argAttrRange = argAttrs.template getAsRange<DictionaryAttr>(); 420 result.append(argAttrRange.begin(), argAttrRange.end()); 421 } else { 422 result.append($_op.getNumArguments(), 423 DictionaryAttr::get(this->getOperation()->getContext())); 424 } 425 } 426 427 /// Return the specified attribute, if present, for the argument at 'index', 428 /// null otherwise. 429 Attribute getArgAttr(unsigned index, StringAttr name) { 430 auto argDict = getArgAttrDict(index); 431 return argDict ? argDict.get(name) : nullptr; 432 } 433 Attribute getArgAttr(unsigned index, StringRef name) { 434 auto argDict = getArgAttrDict(index); 435 return argDict ? argDict.get(name) : nullptr; 436 } 437 438 template <typename AttrClass> 439 AttrClass getArgAttrOfType(unsigned index, StringAttr name) { 440 return getArgAttr(index, name).template dyn_cast_or_null<AttrClass>(); 441 } 442 template <typename AttrClass> 443 AttrClass getArgAttrOfType(unsigned index, StringRef name) { 444 return getArgAttr(index, name).template dyn_cast_or_null<AttrClass>(); 445 } 446 447 /// Set the attributes held by the argument at 'index'. 448 void setArgAttrs(unsigned index, ArrayRef<NamedAttribute> attributes) { 449 function_interface_impl::setArgAttrs($_op, index, attributes); 450 } 451 452 /// Set the attributes held by the argument at 'index'. `attributes` may be 453 /// null, in which case any existing argument attributes are removed. 454 void setArgAttrs(unsigned index, DictionaryAttr attributes) { 455 function_interface_impl::setArgAttrs($_op, index, attributes); 456 } 457 void setAllArgAttrs(ArrayRef<DictionaryAttr> attributes) { 458 assert(attributes.size() == $_op.getNumArguments()); 459 function_interface_impl::setAllArgAttrDicts(this->getOperation(), attributes); 460 } 461 void setAllArgAttrs(ArrayRef<Attribute> attributes) { 462 assert(attributes.size() == $_op.getNumArguments()); 463 function_interface_impl::setAllArgAttrDicts(this->getOperation(), attributes); 464 } 465 void setAllArgAttrs(ArrayAttr attributes) { 466 assert(attributes.size() == $_op.getNumArguments()); 467 this->getOperation()->setAttr(getArgDictAttrName(), attributes); 468 } 469 470 /// If the an attribute exists with the specified name, change it to the new 471 /// value. Otherwise, add a new attribute with the specified name/value. 472 void setArgAttr(unsigned index, StringAttr name, Attribute value) { 473 function_interface_impl::setArgAttr($_op, index, name, value); 474 } 475 void setArgAttr(unsigned index, StringRef name, Attribute value) { 476 setArgAttr(index, 477 StringAttr::get(this->getOperation()->getContext(), name), 478 value); 479 } 480 481 /// Remove the attribute 'name' from the argument at 'index'. Return the 482 /// attribute that was erased, or nullptr if there was no attribute with 483 /// such name. 484 Attribute removeArgAttr(unsigned index, StringAttr name) { 485 return function_interface_impl::removeArgAttr($_op, index, name); 486 } 487 Attribute removeArgAttr(unsigned index, StringRef name) { 488 return removeArgAttr( 489 index, StringAttr::get(this->getOperation()->getContext(), name)); 490 } 491 492 //===------------------------------------------------------------------===// 493 // Result Attributes 494 //===------------------------------------------------------------------===// 495 496 /// Return all of the attributes for the result at 'index'. 497 ArrayRef<NamedAttribute> getResultAttrs(unsigned index) { 498 return function_interface_impl::getResultAttrs(this->getOperation(), index); 499 } 500 501 /// Return an ArrayAttr containing all result attribute dictionaries of this 502 /// function, or nullptr if no result have attributes. 503 ArrayAttr getAllResultAttrs() { 504 return this->getOperation()->template getAttrOfType<ArrayAttr>( 505 getResultDictAttrName()); 506 } 507 /// Return all result attributes of this function. 508 void getAllResultAttrs(SmallVectorImpl<DictionaryAttr> &result) { 509 if (ArrayAttr argAttrs = getAllResultAttrs()) { 510 auto argAttrRange = argAttrs.template getAsRange<DictionaryAttr>(); 511 result.append(argAttrRange.begin(), argAttrRange.end()); 512 } else { 513 result.append($_op.getNumResults(), 514 DictionaryAttr::get(this->getOperation()->getContext())); 515 } 516 } 517 518 /// Return the specified attribute, if present, for the result at 'index', 519 /// null otherwise. 520 Attribute getResultAttr(unsigned index, StringAttr name) { 521 auto argDict = getResultAttrDict(index); 522 return argDict ? argDict.get(name) : nullptr; 523 } 524 Attribute getResultAttr(unsigned index, StringRef name) { 525 auto argDict = getResultAttrDict(index); 526 return argDict ? argDict.get(name) : nullptr; 527 } 528 529 template <typename AttrClass> 530 AttrClass getResultAttrOfType(unsigned index, StringAttr name) { 531 return getResultAttr(index, name).template dyn_cast_or_null<AttrClass>(); 532 } 533 template <typename AttrClass> 534 AttrClass getResultAttrOfType(unsigned index, StringRef name) { 535 return getResultAttr(index, name).template dyn_cast_or_null<AttrClass>(); 536 } 537 538 /// Set the attributes held by the result at 'index'. 539 void setResultAttrs(unsigned index, ArrayRef<NamedAttribute> attributes) { 540 function_interface_impl::setResultAttrs($_op, index, attributes); 541 } 542 543 /// Set the attributes held by the result at 'index'. `attributes` may be 544 /// null, in which case any existing argument attributes are removed. 545 void setResultAttrs(unsigned index, DictionaryAttr attributes) { 546 function_interface_impl::setResultAttrs($_op, index, attributes); 547 } 548 void setAllResultAttrs(ArrayRef<DictionaryAttr> attributes) { 549 assert(attributes.size() == $_op.getNumResults()); 550 function_interface_impl::setAllResultAttrDicts( 551 this->getOperation(), attributes); 552 } 553 void setAllResultAttrs(ArrayRef<Attribute> attributes) { 554 assert(attributes.size() == $_op.getNumResults()); 555 function_interface_impl::setAllResultAttrDicts( 556 this->getOperation(), attributes); 557 } 558 void setAllResultAttrs(ArrayAttr attributes) { 559 assert(attributes.size() == $_op.getNumResults()); 560 this->getOperation()->setAttr(getResultDictAttrName(), attributes); 561 } 562 563 /// If the an attribute exists with the specified name, change it to the new 564 /// value. Otherwise, add a new attribute with the specified name/value. 565 void setResultAttr(unsigned index, StringAttr name, Attribute value) { 566 function_interface_impl::setResultAttr($_op, index, name, value); 567 } 568 void setResultAttr(unsigned index, StringRef name, Attribute value) { 569 setResultAttr(index, 570 StringAttr::get(this->getOperation()->getContext(), name), 571 value); 572 } 573 574 /// Remove the attribute 'name' from the result at 'index'. Return the 575 /// attribute that was erased, or nullptr if there was no attribute with 576 /// such name. 577 Attribute removeResultAttr(unsigned index, StringAttr name) { 578 return function_interface_impl::removeResultAttr($_op, index, name); 579 } 580 581 /// Returns the dictionary attribute corresponding to the argument at 582 /// 'index'. If there are no argument attributes at 'index', a null 583 /// attribute is returned. 584 DictionaryAttr getArgAttrDict(unsigned index) { 585 assert(index < $_op.getNumArguments() && "invalid argument number"); 586 return function_interface_impl::getArgAttrDict(this->getOperation(), index); 587 } 588 589 /// Returns the dictionary attribute corresponding to the result at 'index'. 590 /// If there are no result attributes at 'index', a null attribute is 591 /// returned. 592 DictionaryAttr getResultAttrDict(unsigned index) { 593 assert(index < $_op.getNumResults() && "invalid result number"); 594 return function_interface_impl::getResultAttrDict(this->getOperation(), index); 595 } 596 }]; 597 598 let verify = "return function_interface_impl::verifyTrait(cast<ConcreteOp>($_op));"; 599} 600 601#endif // MLIR_IR_FUNCTIONINTERFACES_TD_ 602