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      "::mlir::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 (::mlir::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 "::mlir::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`. These operands should
138        correspond 1-1 with the successor inputs specified in
139        `getSuccessorRegions`.
140      }],
141      "::mlir::OperandRange", "getSuccessorEntryOperands",
142      (ins "unsigned":$index), [{}], /*defaultImplementation=*/[{
143        auto operandEnd = this->getOperation()->operand_end();
144        return ::mlir::OperandRange(operandEnd, operandEnd);
145      }]
146    >,
147    InterfaceMethod<[{
148        Returns the viable successors of a region at `index`, or the possible
149        successors when branching from the parent op if `index` is None. These
150        are the regions that may be selected during the flow of control. If
151        `index` is None, `operands` is a set of optional attributes that
152        either correspond to a constant value for each operand of this
153        operation, or null if that operand is not a constant. If `index` is
154        valid, `operands` corresponds to the entry values of the region at
155        `index`. Only a region, i.e. a valid `index`, may use the parent
156        operation as a successor. This method allows for describing which
157        regions may be executed when entering an operation, and which regions
158        are executed after having executed another region of the parent op. The
159        successor region must be non-empty.
160      }],
161      "void", "getSuccessorRegions",
162      (ins "::mlir::Optional<unsigned>":$index,
163           "::mlir::ArrayRef<::mlir::Attribute>":$operands,
164           "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions)
165    >,
166    InterfaceMethod<[{
167        Populates `invocationBounds` with the minimum and maximum number of
168        times this operation will invoke the attached regions (assuming the
169        regions yield normally, i.e. do not abort or invoke an infinite loop).
170        The minimum number of invocations is at least 0. If the maximum number
171        of invocations cannot be statically determined, then it will not have a
172        value (i.e., it is set to `llvm::None`).
173
174        `operands` is a set of optional attributes that either correspond to
175        constant values for each operand of this operation or null if that
176        operand is not a constant.
177
178        This method may be called speculatively on operations where the provided
179        operands are not necessarily the same as the operation's current
180        operands. This may occur in analyses that wish to determine "what would
181        be the region invocations if these were the operands?"
182      }],
183      "void", "getRegionInvocationBounds",
184      (ins "::mlir::ArrayRef<::mlir::Attribute>":$operands,
185           "::llvm::SmallVectorImpl<::mlir::InvocationBounds> &"
186             :$invocationBounds), [{}],
187       [{ invocationBounds.append($_op->getNumRegions(),
188                                  ::mlir::InvocationBounds::getUnknown()); }]
189    >,
190    InterfaceMethod<[{
191        This method is called to compare types along control-flow edges. By
192        default, the types are checked as equal.
193      }],
194      "bool", "areTypesCompatible",
195      (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}],
196       [{ return lhs == rhs; }]
197    >,
198  ];
199
200  let verify = [{
201    static_assert(!ConcreteOp::template hasTrait<OpTrait::ZeroRegions>(),
202                  "expected operation to have non-zero regions");
203    return detail::verifyTypesAlongControlFlowEdges($_op);
204  }];
205  let verifyWithRegions = 1;
206
207  let extraClassDeclaration = [{
208    /// Convenience helper in case none of the operands is known.
209    void getSuccessorRegions(Optional<unsigned> index,
210                             SmallVectorImpl<RegionSuccessor> &regions) {
211       SmallVector<Attribute, 2> nullAttrs(getOperation()->getNumOperands());
212       getSuccessorRegions(index, nullAttrs, regions);
213    }
214
215    /// Return `true` if control flow originating from the given region may
216    /// eventually branch back to the same region. (Maybe after passing through
217    /// other regions.)
218    bool isRepetitiveRegion(unsigned index);
219  }];
220}
221
222//===----------------------------------------------------------------------===//
223// RegionBranchTerminatorOpInterface
224//===----------------------------------------------------------------------===//
225
226def RegionBranchTerminatorOpInterface :
227  OpInterface<"RegionBranchTerminatorOpInterface"> {
228  let description = [{
229    This interface provides information for branching terminator operations
230    in the presence of a parent RegionBranchOpInterface implementation. It
231    specifies which operands are passed to which successor region.
232  }];
233  let cppNamespace = "::mlir";
234
235  let methods = [
236    InterfaceMethod<[{
237        Returns a mutable range of operands that are semantically "returned" by
238        passing them to the region successor given by `index`.  If `index` is
239        None, this function returns the operands that are passed as a result to
240        the parent operation.
241      }],
242      "::mlir::MutableOperandRange", "getMutableSuccessorOperands",
243      (ins "::mlir::Optional<unsigned>":$index)
244    >,
245    InterfaceMethod<[{
246        Returns a range of operands that are semantically "returned" by passing
247        them to the region successor given by `index`.  If `index` is None, this
248        function returns the operands that are passed as a result to the parent
249        operation.
250      }],
251      "::mlir::OperandRange", "getSuccessorOperands",
252      (ins "::mlir::Optional<unsigned>":$index), [{}],
253      /*defaultImplementation=*/[{
254        return $_op.getMutableSuccessorOperands(index);
255      }]
256    >
257  ];
258
259  let verify = [{
260    static_assert(ConcreteOp::template hasTrait<OpTrait::IsTerminator>(),
261                  "expected operation to be a terminator");
262    static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroResults>(),
263                  "expected operation to have zero results");
264    static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroSuccessors>(),
265                  "expected operation to have zero successors");
266    return success();
267  }];
268}
269
270//===----------------------------------------------------------------------===//
271// ControlFlow Traits
272//===----------------------------------------------------------------------===//
273
274// Op is "return-like".
275def ReturnLike : NativeOpTrait<"ReturnLike">;
276
277#endif // MLIR_INTERFACES_CONTROLFLOWINTERFACES
278