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