xref: /llvm-project/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td (revision bf68e9047f62c22ca87f9a4a7c59a46b3de06abb)
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 std::nullopt if `operandIndex` isn't a successor operand
71        index.
72      }],
73      "::std::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 (::std::optional<::mlir::BlockArgument> arg = ::mlir::detail::getBranchSuccessorArgument(
78                $_op.getSuccessorOperands(i), operandIndex,
79                opaqueOp->getSuccessor(i)))
80            return arg;
81        }
82        return ::std::nullopt;
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 exhibit
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 and
125    value propagation, where what occurs along control-flow edges is assumed to
126    be side-effect free.
127
128    A "region branch point" indicates a point from which a branch originates. It
129    can indicate either a region of this op or `RegionBranchPoint::parent()`. In
130    the latter case, the branch originates from outside of the op, i.e., when
131    first executing this op.
132
133    A "region successor" indicates the target of a branch. It can indicate
134    either a region of this op or this op. In the former case, the region
135    successor is a region pointer and a range of block arguments to which the
136    "successor operands" are forwarded to. In the latter case, the control flow
137    leaves this op and the region successor is a range of results of this op to
138    which the successor operands are forwarded to.
139
140    By default, successor operands and successor block arguments/successor
141    results must have the same type. `areTypesCompatible` can be implemented to
142    allow non-equal types.
143
144    Example:
145
146    ```
147    %r = scf.for %iv = %lb to %ub step %step iter_args(%a = %b)
148        -> tensor<5xf32> {
149      ...
150      scf.yield %c : tensor<5xf32>
151    }
152    ```
153
154    `scf.for` has one region. The region has two region successors: the region
155    itself and the `scf.for` op. %b is an entry successor operand. %c is a
156    successor operand. %a is a successor block argument. %r is a successor
157    result.
158  }];
159  let cppNamespace = "::mlir";
160
161  let methods = [
162    InterfaceMethod<[{
163        Returns the operands of this operation that are forwarded to the region
164        successor's block arguments or this operation's results when branching
165        to `point`. `point` is guaranteed to be among the successors that are
166        returned by `getEntrySuccessorRegions`/`getSuccessorRegions(parent())`.
167
168        Example: In the above example, this method returns the operand %b of the
169        `scf.for` op, regardless of the value of `point`. I.e., this op always
170        forwards the same operands, regardless of whether the loop has 0 or more
171        iterations.
172      }],
173      "::mlir::OperandRange", "getEntrySuccessorOperands",
174      (ins "::mlir::RegionBranchPoint":$point), [{}],
175      /*defaultImplementation=*/[{
176        auto operandEnd = this->getOperation()->operand_end();
177        return ::mlir::OperandRange(operandEnd, operandEnd);
178      }]
179    >,
180    InterfaceMethod<[{
181        Returns the potential region successors when first executing the op.
182
183        Unlike `getSuccessorRegions`, this method also passes along the
184        constant operands of this op. Based on these, the implementation may
185        filter out certain successors. By default, simply dispatches to
186        `getSuccessorRegions`. `operands` contains an entry for every
187        operand of this op, with a null attribute if the operand has no constant
188        value.
189
190        Note: The control flow does not necessarily have to enter any region of
191        this op.
192
193        Example: In the above example, this method may return two region
194        region successors: the single region of the `scf.for` op and the
195        `scf.for` operation (that implements this interface). If %lb, %ub, %step
196        are constants and it can be determined the loop does not have any
197        iterations, this method may choose to return only this operation.
198        Similarly, if it can be determined that the loop has at least one
199        iteration, this method may choose to return only the region of the loop.
200      }],
201      "void", "getEntrySuccessorRegions",
202      (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands,
203           "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions), [{}],
204      /*defaultImplementation=*/[{
205        $_op.getSuccessorRegions(mlir::RegionBranchPoint::parent(), regions);
206      }]
207    >,
208    InterfaceMethod<[{
209        Returns the potential region successors when branching from `point`.
210        These are the regions that may be selected during the flow of control.
211
212        When `point = RegionBranchPoint::parent()`, this method returns the
213        region successors when entering the operation. Otherwise, this method
214        returns the successor regions when branching from the region indicated
215        by `point`.
216
217        Example: In the above example, this method returns the region of the
218        `scf.for` and this operation for either region branch point (`parent`
219        and the region of the `scf.for`). An implementation may choose to filter
220        out region successors when it is statically known (e.g., by examining
221        the operands of this op) that those successors are not branched to.
222      }],
223      "void", "getSuccessorRegions",
224      (ins "::mlir::RegionBranchPoint":$point,
225           "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions)
226    >,
227    InterfaceMethod<[{
228        Populates `invocationBounds` with the minimum and maximum number of
229        times this operation will invoke the attached regions (assuming the
230        regions yield normally, i.e. do not abort or invoke an infinite loop).
231        The minimum number of invocations is at least 0. If the maximum number
232        of invocations cannot be statically determined, then it will be set to
233        `InvocationBounds::getUnknown()`.
234
235        This method also passes along the constant operands of this op.
236        `operands` contains an entry for every operand of this op, with a null
237        attribute if the operand has no constant value.
238
239        This method may be called speculatively on operations where the provided
240        operands are not necessarily the same as the operation's current
241        operands. This may occur in analyses that wish to determine "what would
242        be the region invocations if these were the operands?"
243      }],
244      "void", "getRegionInvocationBounds",
245      (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands,
246           "::llvm::SmallVectorImpl<::mlir::InvocationBounds> &"
247             :$invocationBounds), [{}],
248      /*defaultImplementation=*/[{
249        invocationBounds.append($_op->getNumRegions(),
250                                ::mlir::InvocationBounds::getUnknown());
251      }]
252    >,
253    InterfaceMethod<[{
254        This method is called to compare types along control-flow edges. By
255        default, the types are checked as equal.
256      }],
257      "bool", "areTypesCompatible",
258      (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}],
259      /*defaultImplementation=*/[{ return lhs == rhs; }]
260    >,
261  ];
262
263  let verify = [{
264    static_assert(!ConcreteOp::template hasTrait<OpTrait::ZeroRegions>(),
265                  "expected operation to have non-zero regions");
266    return detail::verifyTypesAlongControlFlowEdges($_op);
267  }];
268  let verifyWithRegions = 1;
269
270  let extraClassDeclaration = [{
271    /// Return `true` if control flow originating from the given region may
272    /// eventually branch back to the same region. (Maybe after passing through
273    /// other regions.)
274    bool isRepetitiveRegion(unsigned index);
275
276    /// Return `true` if there is a loop in the region branching graph. Only
277    /// reachable regions (starting from the entry regions) are considered.
278    bool hasLoop();
279  }];
280}
281
282//===----------------------------------------------------------------------===//
283// RegionBranchTerminatorOpInterface
284//===----------------------------------------------------------------------===//
285
286def RegionBranchTerminatorOpInterface :
287  OpInterface<"RegionBranchTerminatorOpInterface"> {
288  let description = [{
289    This interface provides information for branching terminator operations
290    in the presence of a parent `RegionBranchOpInterface` implementation. It
291    specifies which operands are passed to which successor region.
292  }];
293  let cppNamespace = "::mlir";
294
295  let methods = [
296    InterfaceMethod<[{
297        Returns a mutable range of operands that are semantically "returned" by
298        passing them to the region successor indicated by `point`.
299      }],
300      "::mlir::MutableOperandRange", "getMutableSuccessorOperands",
301      (ins "::mlir::RegionBranchPoint":$point)
302    >,
303    InterfaceMethod<[{
304        Returns the potential region successors that are branched to after this
305        terminator based on the given constant operands.
306
307        This method also passes along the constant operands of this op.
308        `operands` contains an entry for every operand of this op, with a null
309        attribute if the operand has no constant value.
310
311        The default implementation simply dispatches to the parent
312        `RegionBranchOpInterface`'s `getSuccessorRegions` implementation.
313      }],
314      "void", "getSuccessorRegions",
315      (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands,
316           "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions), [{}],
317      /*defaultImplementation=*/[{
318        ::mlir::Operation *op = $_op;
319        ::llvm::cast<::mlir::RegionBranchOpInterface>(op->getParentOp())
320          .getSuccessorRegions(op->getParentRegion(), regions);
321      }]
322    >,
323  ];
324
325  let verify = [{
326    static_assert(ConcreteOp::template hasTrait<OpTrait::IsTerminator>(),
327                  "expected operation to be a terminator");
328    static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroResults>(),
329                  "expected operation to have zero results");
330    static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroSuccessors>(),
331                  "expected operation to have zero successors");
332    return success();
333  }];
334
335  let extraClassDeclaration = [{
336    // Returns a range of operands that are semantically "returned" by passing
337    // them to the region successor given by `index`.  If `index` is None, this
338    // function returns the operands that are passed as a result to the parent
339    // operation.
340    ::mlir::OperandRange getSuccessorOperands(::mlir::RegionBranchPoint point) {
341      return getMutableSuccessorOperands(point);
342    }
343  }];
344}
345
346def SelectLikeOpInterface : OpInterface<"SelectLikeOpInterface"> {
347  let description = [{
348    This interface provides information for select-like operations, i.e.,
349    operations that forward specific operands to the output, depending on a
350    binary condition.
351
352    If the value of the condition is 1, then the `true` operand is returned,
353    and the third operand is ignored, even if it was poison.
354
355    If the value of the condition is 0, then the `false` operand is returned,
356    and the second operand is ignored, even if it was poison.
357
358    If the condition is poison, then poison is returned.
359
360    Implementing operations can also accept shaped conditions, in which case
361    the operation works element-wise.
362  }];
363  let cppNamespace = "::mlir";
364
365  let methods = [
366    InterfaceMethod<[{
367        Returns the operand that would be chosen for a false condition.
368      }], "::mlir::Value", "getFalseValue", (ins)>,
369    InterfaceMethod<[{
370        Returns the operand that would be chosen for a true condition.
371      }], "::mlir::Value", "getTrueValue", (ins)>,
372    InterfaceMethod<[{
373        Returns the condition operand.
374      }], "::mlir::Value", "getCondition", (ins)>
375  ];
376}
377
378//===----------------------------------------------------------------------===//
379// ControlFlow Traits
380//===----------------------------------------------------------------------===//
381
382// Op is "return-like".
383def ReturnLike : TraitList<[
384    DeclareOpInterfaceMethods<RegionBranchTerminatorOpInterface>,
385    NativeOpTrait<
386        /*name=*/"ReturnLike",
387        /*traits=*/[],
388        /*extraOpDeclaration=*/"",
389        /*extraOpDefinition=*/[{
390          ::mlir::MutableOperandRange $cppClass::getMutableSuccessorOperands(
391            ::mlir::RegionBranchPoint point) {
392            return ::mlir::MutableOperandRange(*this);
393          }
394        }]
395    >
396]>;
397
398#endif // MLIR_INTERFACES_CONTROLFLOWINTERFACES
399