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