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