xref: /llvm-project/mlir/include/mlir/Dialect/ControlFlow/IR/ControlFlowOps.td (revision 6c7cb55f8f8dc833fbd3d0e4261a503cf1e41341)
1//===- ControlFlowOps.td - ControlFlow operations ----------*- 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 the operations within the ControlFlow
10// dialect.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef MLIR_DIALECTS_CONTROLFLOW_IR_CONTROLFLOWOPS_TD
15#define MLIR_DIALECTS_CONTROLFLOW_IR_CONTROLFLOWOPS_TD
16
17include "mlir/IR/EnumAttr.td"
18include "mlir/IR/OpAsmInterface.td"
19include "mlir/Interfaces/ControlFlowInterfaces.td"
20include "mlir/Interfaces/SideEffectInterfaces.td"
21
22def ControlFlow_Dialect : Dialect {
23  let name = "cf";
24  let cppNamespace = "::mlir::cf";
25  let dependentDialects = ["arith::ArithDialect"];
26  let description = [{
27    This dialect contains low-level, i.e. non-region based, control flow
28    constructs. These constructs generally represent control flow directly
29    on SSA blocks of a control flow graph.
30  }];
31}
32
33class CF_Op<string mnemonic, list<Trait> traits = []> :
34    Op<ControlFlow_Dialect, mnemonic, traits>;
35
36//===----------------------------------------------------------------------===//
37// AssertOp
38//===----------------------------------------------------------------------===//
39
40def AssertOp : CF_Op<"assert",
41    [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
42  let summary = "Assert operation with message attribute";
43  let description = [{
44    Assert operation at runtime with single boolean operand and an error
45    message attribute.
46    If the argument is `true` this operation has no effect. Otherwise, the
47    program execution will abort. The provided error message may be used by a
48    runtime to propagate the error to the user.
49
50    Example:
51
52    ```mlir
53    cf.assert %b, "Expected ... to be true"
54    ```
55  }];
56
57  let arguments = (ins I1:$arg, StrAttr:$msg);
58
59  let assemblyFormat = "$arg `,` $msg attr-dict";
60  let hasCanonicalizeMethod = 1;
61}
62
63//===----------------------------------------------------------------------===//
64// BranchOp
65//===----------------------------------------------------------------------===//
66
67def BranchOp : CF_Op<"br", [
68    DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>,
69    Pure, Terminator
70  ]> {
71  let summary = "Branch operation";
72  let description = [{
73    The `cf.br` operation represents a direct branch operation to a given
74    block. The operands of this operation are forwarded to the successor block,
75    and the number and type of the operands must match the arguments of the
76    target block.
77
78    Example:
79
80    ```mlir
81    ^bb2:
82      %2 = call @someFn()
83      cf.br ^bb3(%2 : tensor<*xf32>)
84    ^bb3(%3: tensor<*xf32>):
85    ```
86  }];
87
88  let arguments = (ins Variadic<AnyType>:$destOperands);
89  let successors = (successor AnySuccessor:$dest);
90
91  let builders = [
92    OpBuilder<(ins "Block *":$dest,
93                   CArg<"ValueRange", "{}">:$destOperands), [{
94      $_state.addSuccessors(dest);
95      $_state.addOperands(destOperands);
96    }]>];
97
98  let extraClassDeclaration = [{
99    void setDest(Block *block);
100
101    /// Erase the operand at 'index' from the operand list.
102    void eraseOperand(unsigned index);
103  }];
104
105  let hasCanonicalizeMethod = 1;
106  let assemblyFormat = [{
107    $dest (`(` $destOperands^ `:` type($destOperands) `)`)? attr-dict
108  }];
109}
110
111//===----------------------------------------------------------------------===//
112// CondBranchOp
113//===----------------------------------------------------------------------===//
114
115def CondBranchOp : CF_Op<"cond_br",
116    [AttrSizedOperandSegments,
117     DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>,
118     Pure, Terminator]> {
119  let summary = "Conditional branch operation";
120  let description = [{
121    The `cf.cond_br` terminator operation represents a conditional branch on a
122    boolean (1-bit integer) value. If the bit is set, then the first destination
123    is jumped to; if it is false, the second destination is chosen. The count
124    and types of operands must align with the arguments in the corresponding
125    target blocks.
126
127    The MLIR conditional branch operation is not allowed to target the entry
128    block for a region. The two destinations of the conditional branch operation
129    are allowed to be the same.
130
131    The following example illustrates a function with a conditional branch
132    operation that targets the same block.
133
134    Example:
135
136    ```mlir
137    func.func @select(%a: i32, %b: i32, %flag: i1) -> i32 {
138      // Both targets are the same, operands differ
139      cf.cond_br %flag, ^bb1(%a : i32), ^bb1(%b : i32)
140
141    ^bb1(%x : i32) :
142      return %x : i32
143    }
144    ```
145  }];
146
147  let arguments = (ins I1:$condition,
148                       Variadic<AnyType>:$trueDestOperands,
149                       Variadic<AnyType>:$falseDestOperands);
150  let successors = (successor AnySuccessor:$trueDest, AnySuccessor:$falseDest);
151
152  let builders = [
153    OpBuilder<(ins "Value":$condition, "Block *":$trueDest,
154      "ValueRange":$trueOperands, "Block *":$falseDest,
155      "ValueRange":$falseOperands), [{
156      build($_builder, $_state, condition, trueOperands, falseOperands, trueDest,
157            falseDest);
158    }]>,
159    OpBuilder<(ins "Value":$condition, "Block *":$trueDest,
160      "Block *":$falseDest, CArg<"ValueRange", "{}">:$falseOperands), [{
161      build($_builder, $_state, condition, trueDest, ValueRange(), falseDest,
162            falseOperands);
163    }]>];
164
165  let extraClassDeclaration = [{
166    // These are the indices into the dests list.
167    enum { trueIndex = 0, falseIndex = 1 };
168
169    // Accessors for operands to the 'true' destination.
170    Value getTrueOperand(unsigned idx) {
171      assert(idx < getNumTrueOperands());
172      return getOperand(getTrueDestOperandIndex() + idx);
173    }
174
175    void setTrueOperand(unsigned idx, Value value) {
176      assert(idx < getNumTrueOperands());
177      setOperand(getTrueDestOperandIndex() + idx, value);
178    }
179
180    unsigned getNumTrueOperands()  { return getTrueOperands().size(); }
181
182    /// Erase the operand at 'index' from the true operand list.
183    void eraseTrueOperand(unsigned index)  {
184      getTrueDestOperandsMutable().erase(index);
185    }
186
187    // Accessors for operands to the 'false' destination.
188    Value getFalseOperand(unsigned idx) {
189      assert(idx < getNumFalseOperands());
190      return getOperand(getFalseDestOperandIndex() + idx);
191    }
192    void setFalseOperand(unsigned idx, Value value) {
193      assert(idx < getNumFalseOperands());
194      setOperand(getFalseDestOperandIndex() + idx, value);
195    }
196
197    operand_range getTrueOperands() { return getTrueDestOperands(); }
198    operand_range getFalseOperands() { return getFalseDestOperands(); }
199
200    unsigned getNumFalseOperands() { return getFalseOperands().size(); }
201
202    /// Erase the operand at 'index' from the false operand list.
203    void eraseFalseOperand(unsigned index) {
204      getFalseDestOperandsMutable().erase(index);
205    }
206
207  private:
208    /// Get the index of the first true destination operand.
209    unsigned getTrueDestOperandIndex() { return 1; }
210
211    /// Get the index of the first false destination operand.
212    unsigned getFalseDestOperandIndex() {
213      return getTrueDestOperandIndex() + getNumTrueOperands();
214    }
215  }];
216
217  let hasCanonicalizer = 1;
218  let assemblyFormat = [{
219    $condition `,`
220    $trueDest (`(` $trueDestOperands^ `:` type($trueDestOperands) `)`)? `,`
221    $falseDest (`(` $falseDestOperands^ `:` type($falseDestOperands) `)`)?
222    attr-dict
223  }];
224}
225
226//===----------------------------------------------------------------------===//
227// SwitchOp
228//===----------------------------------------------------------------------===//
229
230def SwitchOp : CF_Op<"switch",
231    [AttrSizedOperandSegments,
232     DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>,
233     Pure, Terminator]> {
234  let summary = "Switch operation";
235  let description = [{
236    The `cf.switch` terminator operation represents a switch on a signless integer
237    value. If the flag matches one of the specified cases, then the
238    corresponding destination is jumped to. If the flag does not match any of
239    the cases, the default destination is jumped to. The count and types of
240    operands must align with the arguments in the corresponding target blocks.
241
242    Example:
243
244    ```mlir
245    cf.switch %flag : i32, [
246      default: ^bb1(%a : i32),
247      42: ^bb1(%b : i32),
248      43: ^bb3(%c : i32)
249    ]
250    ```
251  }];
252
253  let arguments = (ins
254    AnyInteger:$flag,
255    Variadic<AnyType>:$defaultOperands,
256    VariadicOfVariadic<AnyType, "case_operand_segments">:$caseOperands,
257    OptionalAttr<AnyIntElementsAttr>:$case_values,
258    DenseI32ArrayAttr:$case_operand_segments
259  );
260  let successors = (successor
261    AnySuccessor:$defaultDestination,
262    VariadicSuccessor<AnySuccessor>:$caseDestinations
263  );
264  let builders = [
265    OpBuilder<(ins "Value":$flag,
266      "Block *":$defaultDestination,
267      "ValueRange":$defaultOperands,
268      CArg<"ArrayRef<APInt>", "{}">:$caseValues,
269      CArg<"BlockRange", "{}">:$caseDestinations,
270      CArg<"ArrayRef<ValueRange>", "{}">:$caseOperands)>,
271    OpBuilder<(ins "Value":$flag,
272      "Block *":$defaultDestination,
273      "ValueRange":$defaultOperands,
274      CArg<"ArrayRef<int32_t>", "{}">:$caseValues,
275      CArg<"BlockRange", "{}">:$caseDestinations,
276      CArg<"ArrayRef<ValueRange>", "{}">:$caseOperands)>,
277    OpBuilder<(ins "Value":$flag,
278      "Block *":$defaultDestination,
279      "ValueRange":$defaultOperands,
280      CArg<"DenseIntElementsAttr", "{}">:$caseValues,
281      CArg<"BlockRange", "{}">:$caseDestinations,
282      CArg<"ArrayRef<ValueRange>", "{}">:$caseOperands)>
283  ];
284
285  let assemblyFormat = [{
286    $flag `:` type($flag) `,` `[` `\n`
287      custom<SwitchOpCases>(ref(type($flag)),$defaultDestination,
288                            $defaultOperands,
289                            type($defaultOperands),
290                            $case_values,
291                            $caseDestinations,
292                            $caseOperands,
293                            type($caseOperands))
294   `]`
295    attr-dict
296  }];
297
298  let extraClassDeclaration = [{
299    /// Return the operands for the case destination block at the given index.
300    OperandRange getCaseOperands(unsigned index) {
301      return getCaseOperands()[index];
302    }
303
304    /// Return a mutable range of operands for the case destination block at the
305    /// given index.
306    MutableOperandRange getCaseOperandsMutable(unsigned index) {
307      return getCaseOperandsMutable()[index];
308    }
309  }];
310
311  let hasCanonicalizer = 1;
312  let hasVerifier = 1;
313}
314
315#endif // MLIR_DIALECTS_CONTROLFLOW_IR_CONTROLFLOWOPS_TD
316