1//===-- ControlFlowInterfaces.td - ControlFlow 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 a set of interfaces that can be used to define information
10// about control flow operations, e.g. branches.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef MLIR_INTERFACES_CONTROLFLOWINTERFACES
15#define MLIR_INTERFACES_CONTROLFLOWINTERFACES
16
17include "mlir/IR/OpBase.td"
18
19//===----------------------------------------------------------------------===//
20// BranchOpInterface
21//===----------------------------------------------------------------------===//
22
23def BranchOpInterface : OpInterface<"BranchOpInterface"> {
24  let description = [{
25    This interface provides information for branching terminator operations,
26    i.e. terminator operations with successors.
27
28    This interface is meant to model well-defined cases of control-flow of
29    value propagation, where what occurs along control-flow edges is assumed to
30    be side-effect free. For example, corresponding successor operands and
31    successor block arguments may have different types. In such cases,
32    `areTypesCompatible` can be implemented to compare types along control-flow
33    edges. By default, type equality is used.
34  }];
35  let cppNamespace = "::mlir";
36
37  let methods = [
38    InterfaceMethod<[{
39        Returns the operands that correspond to the arguments of the successor
40        at the given index. It consists of a number of operands that are
41        internally produced by the operation, followed by a range of operands
42        that are forwarded. An example operation making use of produced
43        operands would be:
44
45        ```mlir
46        invoke %function(%0)
47            label ^success ^error(%1 : i32)
48
49        ^error(%e: !error, %arg0: i32):
50            ...
51        ```
52
53        The operand that would map to the `^error`s `%e` operand is produced
54        by the `invoke` operation, while `%1` is a forwarded operand that maps
55        to `%arg0` in the successor.
56
57        Produced operands always map to the first few block arguments of the
58        successor, followed by the forwarded operands. Mapping them in any
59        other order is not supported by the interface.
60
61        By having the forwarded operands last allows users of the interface
62        to append more forwarded operands to the branch operation without
63        interfering with other successor operands.
64      }],
65      "::mlir::SuccessorOperands", "getSuccessorOperands",
66      (ins "unsigned":$index)
67    >,
68    InterfaceMethod<[{
69        Returns the `BlockArgument` corresponding to operand `operandIndex` in
70        some successor, or None if `operandIndex` isn't a successor operand
71        index.
72      }],
73      "::llvm::Optional<::mlir::BlockArgument>", "getSuccessorBlockArgument",
74      (ins "unsigned":$operandIndex), [{
75        ::mlir::Operation *opaqueOp = $_op;
76        for (unsigned i = 0, e = opaqueOp->getNumSuccessors(); i != e; ++i) {
77          if (::llvm::Optional<::mlir::BlockArgument> arg = ::mlir::detail::getBranchSuccessorArgument(
78                $_op.getSuccessorOperands(i), operandIndex,
79                opaqueOp->getSuccessor(i)))
80            return arg;
81        }
82        return ::llvm::None;
83      }]
84    >,
85    InterfaceMethod<[{
86        Returns the successor that would be chosen with the given constant
87        operands. Returns nullptr if a single successor could not be chosen.
88      }],
89      "::mlir::Block *", "getSuccessorForOperands",
90      (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands), [{}],
91      /*defaultImplementation=*/[{ return nullptr; }]
92    >,
93    InterfaceMethod<[{
94        This method is called to compare types along control-flow edges. By
95        default, the types are checked as equal.
96      }],
97      "bool", "areTypesCompatible",
98      (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}],
99       [{ return lhs == rhs; }]
100    >,
101  ];
102
103  let verify = [{
104    auto concreteOp = ::mlir::cast<ConcreteOp>($_op);
105    for (unsigned i = 0, e = $_op->getNumSuccessors(); i != e; ++i) {
106      ::mlir::SuccessorOperands operands = concreteOp.getSuccessorOperands(i);
107      if (::mlir::failed(::mlir::detail::verifyBranchSuccessorOperands($_op, i, operands)))
108        return ::mlir::failure();
109    }
110    return ::mlir::success();
111  }];
112}
113
114//===----------------------------------------------------------------------===//
115// RegionBranchOpInterface
116//===----------------------------------------------------------------------===//
117
118def RegionBranchOpInterface : OpInterface<"RegionBranchOpInterface"> {
119  let description = [{
120    This interface provides information for region operations that contain
121    branching behavior between held regions, i.e. this interface allows for
122    expressing control flow information for region holding operations.
123
124    This interface is meant to model well-defined cases of control-flow of
125    value propagation, where what occurs along control-flow edges is assumed to
126    be side-effect free. For example, corresponding successor operands and
127    successor block arguments may have different types. In such cases,
128    `areTypesCompatible` can be implemented to compare types along control-flow
129    edges. By default, type equality is used.
130  }];
131  let cppNamespace = "::mlir";
132
133  let methods = [
134    InterfaceMethod<[{
135        Returns the operands of this operation used as the entry arguments when
136        entering the region at `index`, which was specified as a successor of
137        this operation by `getSuccessorRegions`, or the operands forwarded to
138        the operation's results when it branches back to itself. These operands
139        should correspond 1-1 with the successor inputs specified in
140        `getSuccessorRegions`.
141      }],
142      "::mlir::OperandRange", "getSuccessorEntryOperands",
143      (ins "::llvm::Optional<unsigned>":$index), [{}],
144      /*defaultImplementation=*/[{
145        auto operandEnd = this->getOperation()->operand_end();
146        return ::mlir::OperandRange(operandEnd, operandEnd);
147      }]
148    >,
149    InterfaceMethod<[{
150        Returns the viable successors of a region at `index`, or the possible
151        successors when branching from the parent op if `index` is None. These
152        are the regions that may be selected during the flow of control. If
153        `index` is None, `operands` is a set of optional attributes that
154        either correspond to a constant value for each operand of this
155        operation, or null if that operand is not a constant. If `index` is
156        valid, `operands` corresponds to the entry values of the region at
157        `index`. Only a region, i.e. a valid `index`, may use the parent
158        operation as a successor. This method allows for describing which
159        regions may be executed when entering an operation, and which regions
160        are executed after having executed another region of the parent op. The
161        successor region must be non-empty.
162      }],
163      "void", "getSuccessorRegions",
164      (ins "::llvm::Optional<unsigned>":$index,
165           "::llvm::ArrayRef<::mlir::Attribute>":$operands,
166           "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions)
167    >,
168    InterfaceMethod<[{
169        Populates `invocationBounds` with the minimum and maximum number of
170        times this operation will invoke the attached regions (assuming the
171        regions yield normally, i.e. do not abort or invoke an infinite loop).
172        The minimum number of invocations is at least 0. If the maximum number
173        of invocations cannot be statically determined, then it will not have a
174        value (i.e., it is set to `llvm::None`).
175
176        `operands` is a set of optional attributes that either correspond to
177        constant values for each operand of this operation or null if that
178        operand is not a constant.
179
180        This method may be called speculatively on operations where the provided
181        operands are not necessarily the same as the operation's current
182        operands. This may occur in analyses that wish to determine "what would
183        be the region invocations if these were the operands?"
184      }],
185      "void", "getRegionInvocationBounds",
186      (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands,
187           "::llvm::SmallVectorImpl<::mlir::InvocationBounds> &"
188             :$invocationBounds), [{}],
189       [{ invocationBounds.append($_op->getNumRegions(),
190                                  ::mlir::InvocationBounds::getUnknown()); }]
191    >,
192    InterfaceMethod<[{
193        This method is called to compare types along control-flow edges. By
194        default, the types are checked as equal.
195      }],
196      "bool", "areTypesCompatible",
197      (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}],
198       [{ return lhs == rhs; }]
199    >,
200  ];
201
202  let verify = [{
203    static_assert(!ConcreteOp::template hasTrait<OpTrait::ZeroRegions>(),
204                  "expected operation to have non-zero regions");
205    return detail::verifyTypesAlongControlFlowEdges($_op);
206  }];
207  let verifyWithRegions = 1;
208
209  let extraClassDeclaration = [{
210    /// Convenience helper in case none of the operands is known.
211    void getSuccessorRegions(Optional<unsigned> index,
212                             SmallVectorImpl<RegionSuccessor> &regions);
213
214    /// Return `true` if control flow originating from the given region may
215    /// eventually branch back to the same region. (Maybe after passing through
216    /// other regions.)
217    bool isRepetitiveRegion(unsigned index);
218  }];
219}
220
221//===----------------------------------------------------------------------===//
222// RegionBranchTerminatorOpInterface
223//===----------------------------------------------------------------------===//
224
225def RegionBranchTerminatorOpInterface :
226  OpInterface<"RegionBranchTerminatorOpInterface"> {
227  let description = [{
228    This interface provides information for branching terminator operations
229    in the presence of a parent RegionBranchOpInterface implementation. It
230    specifies which operands are passed to which successor region.
231  }];
232  let cppNamespace = "::mlir";
233
234  let methods = [
235    InterfaceMethod<[{
236        Returns a mutable range of operands that are semantically "returned" by
237        passing them to the region successor given by `index`.  If `index` is
238        None, this function returns the operands that are passed as a result to
239        the parent operation.
240      }],
241      "::mlir::MutableOperandRange", "getMutableSuccessorOperands",
242      (ins "::llvm::Optional<unsigned>":$index)
243    >,
244    InterfaceMethod<[{
245        Returns a range of operands that are semantically "returned" by passing
246        them to the region successor given by `index`.  If `index` is None, this
247        function returns the operands that are passed as a result to the parent
248        operation.
249      }],
250      "::mlir::OperandRange", "getSuccessorOperands",
251      (ins "::llvm::Optional<unsigned>":$index), [{}],
252      /*defaultImplementation=*/[{
253        return $_op.getMutableSuccessorOperands(index);
254      }]
255    >
256  ];
257
258  let verify = [{
259    static_assert(ConcreteOp::template hasTrait<OpTrait::IsTerminator>(),
260                  "expected operation to be a terminator");
261    static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroResults>(),
262                  "expected operation to have zero results");
263    static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroSuccessors>(),
264                  "expected operation to have zero successors");
265    return success();
266  }];
267}
268
269//===----------------------------------------------------------------------===//
270// ControlFlow Traits
271//===----------------------------------------------------------------------===//
272
273// Op is "return-like".
274def ReturnLike : NativeOpTrait<"ReturnLike">;
275
276#endif // MLIR_INTERFACES_CONTROLFLOWINTERFACES
277