1//===- SCFTransformOps.td - SCF (loop) transformation ops --*- 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#ifndef SCF_TRANSFORM_OPS 10#define SCF_TRANSFORM_OPS 11 12include "mlir/Dialect/Transform/IR/TransformDialect.td" 13include "mlir/Dialect/Transform/Interfaces/TransformInterfaces.td" 14include "mlir/Dialect/Transform/IR/TransformTypes.td" 15include "mlir/Interfaces/SideEffectInterfaces.td" 16include "mlir/IR/OpBase.td" 17 18def ApplyForLoopCanonicalizationPatternsOp : Op<Transform_Dialect, 19 "apply_patterns.scf.for_loop_canonicalization", 20 [DeclareOpInterfaceMethods<PatternDescriptorOpInterface>]> { 21 let description = [{ 22 Collects patterns for canonicalizing operations inside SCF loop bodies. 23 At the moment, only affine.min/max computations with iteration variables, 24 loop bounds and loop steps are canonicalized. 25 }]; 26 27 let assemblyFormat = "attr-dict"; 28} 29 30def ApplySCFStructuralConversionPatternsOp : Op<Transform_Dialect, 31 "apply_conversion_patterns.scf.structural_conversions", 32 [DeclareOpInterfaceMethods<ConversionPatternDescriptorOpInterface, 33 ["populateConversionTargetRules"]>]> { 34 let description = [{ 35 Collects patterns for performing structural conversions of SCF operations. 36 }]; 37 38 let assemblyFormat = "attr-dict"; 39} 40 41def ApplySCFToControlFlowPatternsOp : Op<Transform_Dialect, 42 "apply_conversion_patterns.scf.scf_to_control_flow", 43 [DeclareOpInterfaceMethods<ConversionPatternDescriptorOpInterface>]> { 44 let description = [{ 45 Collects patterns that lower structured control flow ops to unstructured 46 control flow. 47 }]; 48 49 let assemblyFormat = "attr-dict"; 50} 51 52def Transform_ScfForOp : Transform_ConcreteOpType<"scf.for">; 53 54def ForallToForOp : Op<Transform_Dialect, "loop.forall_to_for", 55 [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 56 DeclareOpInterfaceMethods<TransformOpInterface>]> { 57 let summary = "Converts scf.forall into a nest of scf.for operations"; 58 let description = [{ 59 Converts the `scf.forall` operation pointed to by the given handle into a 60 set of nested `scf.for` operations. Each new operation corresponds to one 61 induction variable of the original "multifor" loop. 62 63 The operand handle must be associated with exactly one payload operation. 64 65 Loops with shared outputs are currently not supported. 66 67 #### Return Modes 68 69 Consumes the operand handle. Produces a silenceable failure if the operand 70 is not associated with a single `scf.forall` payload operation. 71 Returns as many handles as the given `forall` op has induction variables 72 that are associated with the generated `scf.for` loops. 73 Produces a silenceable failure if another number of resulting handles is 74 requested. 75 }]; 76 let arguments = (ins TransformHandleTypeInterface:$target); 77 let results = (outs Variadic<TransformHandleTypeInterface>:$transformed); 78 79 let assemblyFormat = "$target attr-dict `:` functional-type(operands, results)"; 80} 81 82def ForallToParallelOp : Op<Transform_Dialect, "loop.forall_to_parallel", 83 [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 84 DeclareOpInterfaceMethods<TransformOpInterface>]> { 85 let summary = "Converts scf.forall into a nest of scf.for operations"; 86 let description = [{ 87 Converts the `scf.forall` operation pointed to by the given handle into an 88 `scf.parallel` operation. 89 90 The operand handle must be associated with exactly one payload operation. 91 92 Loops with outputs are not supported. 93 94 #### Return Modes 95 96 Consumes the operand handle. Produces a silenceable failure if the operand 97 is not associated with a single `scf.forall` payload operation. 98 Returns a handle to the new `scf.parallel` operation. 99 Produces a silenceable failure if another number of resulting handles is 100 requested. 101 }]; 102 let arguments = (ins TransformHandleTypeInterface:$target); 103 let results = (outs Variadic<TransformHandleTypeInterface>:$transformed); 104 105 let assemblyFormat = "$target attr-dict `:` functional-type(operands, results)"; 106} 107 108def LoopOutlineOp : Op<Transform_Dialect, "loop.outline", 109 [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 110 DeclareOpInterfaceMethods<TransformOpInterface>]> { 111 let summary = "Outlines a loop into a named function"; 112 let description = [{ 113 Moves the loop into a separate function with the specified name and replaces 114 the loop in the Payload IR with a call to that function. Takes care of 115 forwarding values that are used in the loop as function arguments. If the 116 operand is associated with more than one loop, each loop will be outlined 117 into a separate function. The provided name is used as a _base_ for forming 118 actual function names following `SymbolTable` auto-renaming scheme to avoid 119 duplicate symbols. Expects that all ops in the Payload IR have a 120 `SymbolTable` ancestor (typically true because of the top-level module). 121 122 #### Return Modes 123 124 Returns a handle to the list of outlined functions and a handle to the 125 corresponding function call operations in the same order as the operand 126 handle. 127 128 Produces a definite failure if outlining failed for any of the targets. 129 }]; 130 131 // Note that despite the name of the transform operation and related utility 132 // functions, the actual implementation does not require the operation to be 133 // a loop. 134 let arguments = (ins TransformHandleTypeInterface:$target, 135 StrAttr:$func_name); 136 let results = (outs TransformHandleTypeInterface:$function, 137 TransformHandleTypeInterface:$call); 138 139 let assemblyFormat = 140 "$target attr-dict `:` functional-type(operands, results)"; 141} 142 143def LoopPeelOp : Op<Transform_Dialect, "loop.peel", 144 [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 145 TransformOpInterface, TransformEachOpTrait]> { 146 let summary = "Peels the first or last iteration of the loop"; 147 let description = [{ 148 Rewrite the given loop with a main loop and a partial (first or last) loop. 149 When the `peelFront` option is set to true, the first iteration is peeled off. 150 Otherwise, updates the given loop so that its step evenly divides its range and puts 151 the remaining iteration into a separate loop or a conditional. 152 153 In the absence of sufficient static information, this op may peel a loop, 154 even if the step always divides the range evenly at runtime. 155 156 #### Return modes 157 158 This operation ignores non-scf::ForOp ops and drops them in the return. 159 The op returns two loops, the peeled loop which has trip count divisible 160 by the step, and the remainder loop. 161 162 When `peelFront` is true, the first result (remainder loop) executes all 163 but the first iteration of the target loop. The second result (peeled 164 loop) corresponds to the first iteration of the loop which can be 165 canonicalized away in the following optimizations. 166 167 When `peelFront` is false, the first result (peeled loop) is the portion 168 of the target loop with the highest upper bound that is divisible by the 169 step. The second result (remainder loop) contains the remaining iterations. 170 171 Note that even though the Payload IR modification may be performed 172 in-place, this operation consumes the operand handle and produces a new one. 173 174 #### Return Modes 175 176 Produces a definite failure if peeling fails. 177 }]; 178 179 let arguments = 180 (ins Transform_ScfForOp:$target, 181 DefaultValuedAttr<BoolAttr, "false">:$peel_front, 182 DefaultValuedAttr<BoolAttr, "false">:$fail_if_already_divisible); 183 let results = (outs TransformHandleTypeInterface:$peeled_loop, 184 TransformHandleTypeInterface:$remainder_loop); 185 186 let assemblyFormat = 187 "$target attr-dict `:` functional-type(operands, results)"; 188 189 let extraClassDeclaration = [{ 190 ::mlir::DiagnosedSilenceableFailure applyToOne( 191 ::mlir::transform::TransformRewriter &rewriter, 192 ::mlir::scf::ForOp target, 193 ::mlir::transform::ApplyToEachResultList &results, 194 ::mlir::transform::TransformState &state); 195 }]; 196} 197 198def LoopPipelineOp : Op<Transform_Dialect, "loop.pipeline", 199 [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 200 TransformOpInterface, TransformEachOpTrait]> { 201 let summary = "Applies software pipelining to the loop"; 202 let description = [{ 203 Transforms the given loops one by one to achieve software pipelining for 204 each of them. That is, performs some amount of reads from memory before the 205 loop rather than inside the loop, the same amount of writes into memory 206 after the loop, and updates each iteration to read the data for a following 207 iteration rather than the current one. 208 209 The amount is specified by the attributes. 210 211 The values read and about to be stored are transferred as loop iteration 212 arguments. Currently supports memref and vector transfer operations as 213 memory reads/writes. 214 215 #### Return modes 216 217 This operation ignores non-scf::For ops and drops them in the return. 218 If all the operations referred to by the `target` PDLOperation pipeline 219 properly, the transform succeeds. Otherwise the transform produces a 220 silenceable failure. The return handle points to only the subset of 221 successfully produced pipelined loops, which can be empty. 222 }]; 223 224 let arguments = (ins Transform_ScfForOp:$target, 225 DefaultValuedAttr<I64Attr, "1">:$iteration_interval, 226 DefaultValuedAttr<I64Attr, "10">:$read_latency); 227 let results = (outs TransformHandleTypeInterface:$transformed); 228 229 let assemblyFormat = 230 "$target attr-dict `:` functional-type(operands, results)"; 231 232 let extraClassDeclaration = [{ 233 ::mlir::DiagnosedSilenceableFailure applyToOne( 234 ::mlir::transform::TransformRewriter &rewriter, 235 ::mlir::scf::ForOp target, 236 ::mlir::transform::ApplyToEachResultList &results, 237 ::mlir::transform::TransformState &state); 238 }]; 239} 240 241def LoopPromoteIfOneIterationOp : Op<Transform_Dialect, 242 "loop.promote_if_one_iteration", [ 243 DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, 244 TransformOpInterface, TransformEachOpTrait]> { 245 let summary = "Promote loop if it has one iteration"; 246 let description = [{ 247 Promotes the given target loop op if it has a single iteration. I.e., the 248 loop op is removed and only the body remains. 249 250 #### Return modes 251 252 This transform fails if the target is mapped to ops that are loops. Ops are 253 considered loops if they implement the `LoopLikeOpInterface`. Otherwise, 254 this transform always succeeds. The transform consumes the target handle and 255 modifies the payload. 256 }]; 257 258 let arguments = (ins TransformHandleTypeInterface:$target); 259 let results = (outs); 260 let assemblyFormat = "$target attr-dict `:` type($target)"; 261 262 let extraClassDeclaration = [{ 263 ::mlir::DiagnosedSilenceableFailure applyToOne( 264 ::mlir::transform::TransformRewriter &rewriter, 265 ::mlir::LoopLikeOpInterface target, 266 ::mlir::transform::ApplyToEachResultList &results, 267 ::mlir::transform::TransformState &state); 268 }]; 269} 270 271def LoopUnrollOp : Op<Transform_Dialect, "loop.unroll", 272 [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 273 TransformOpInterface, TransformEachOpTrait]> { 274 let summary = "Unrolls the given loop with the given unroll factor"; 275 let description = [{ 276 Unrolls each loop associated with the given handle to have up to the given 277 number of loop body copies per iteration. If the unroll factor is larger 278 than the loop trip count, the latter is used as the unroll factor instead. 279 280 #### Return modes 281 282 This operation ignores non-`scf.for`, non-`affine.for` ops and drops them 283 in the return. If all the operations referred to by the `target` operand 284 unroll properly, the transform succeeds. Otherwise the transform produces a 285 silenceable failure. 286 287 Does not return handles as the operation may result in the loop being 288 removed after a full unrolling. 289 }]; 290 291 let arguments = (ins TransformHandleTypeInterface:$target, 292 ConfinedAttr<I64Attr, [IntPositive]>:$factor); 293 294 let assemblyFormat = "$target attr-dict `:` type($target)"; 295 296 let extraClassDeclaration = [{ 297 ::mlir::DiagnosedSilenceableFailure applyToOne( 298 ::mlir::transform::TransformRewriter &rewriter, 299 ::mlir::Operation *target, 300 ::mlir::transform::ApplyToEachResultList &results, 301 ::mlir::transform::TransformState &state); 302 }]; 303} 304 305def LoopUnrollAndJamOp : Op<Transform_Dialect, "loop.unroll_and_jam", 306 [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 307 TransformOpInterface, TransformEachOpTrait]> { 308 let summary = "Unrolls and jam the given loop with the given unroll factor"; 309 let description = [{ 310 Unrolls & jams each loop associated with the given handle to have up to the given 311 number of loop body copies per iteration. If the unroll factor is larger 312 than the loop trip count, the latter is used as the unroll factor instead. 313 314 #### Return modes 315 316 This operation ignores non-`scf.for`, non-`affine.for` ops and drops them 317 in the return. If all the operations referred to by the `target` operand 318 unroll properly, the transform succeeds. Otherwise the transform produces a 319 silenceable failure. 320 321 Does not return handles as the operation may result in the loop being 322 removed after a full unrolling. 323 }]; 324 325 let arguments = (ins TransformHandleTypeInterface:$target, 326 ConfinedAttr<I64Attr, [IntPositive]>:$factor); 327 328 let assemblyFormat = "$target attr-dict `:` type($target)"; 329 330 let extraClassDeclaration = [{ 331 ::mlir::DiagnosedSilenceableFailure applyToOne( 332 ::mlir::transform::TransformRewriter &rewriter, 333 ::mlir::Operation *target, 334 ::mlir::transform::ApplyToEachResultList &results, 335 ::mlir::transform::TransformState &state); 336 }]; 337} 338 339def LoopCoalesceOp : Op<Transform_Dialect, "loop.coalesce", [ 340 FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 341 TransformOpInterface, TransformEachOpTrait]> { 342 let summary = "Coalesces the perfect loop nest enclosed by a given loop"; 343 let description = [{ 344 Given a perfect loop nest identified by the outermost loop, 345 perform loop coalescing in a bottom-up one-by-one manner. 346 347 #### Return modes 348 349 The return handle points to the coalesced loop if coalescing happens, or 350 the given input loop if coalescing does not happen. 351 }]; 352 let arguments = (ins TransformHandleTypeInterface:$target); 353 let results = (outs TransformHandleTypeInterface:$transformed); 354 355 let assemblyFormat = 356 "$target attr-dict `:` functional-type($target, $transformed)"; 357 358 let extraClassDeclaration = [{ 359 ::mlir::DiagnosedSilenceableFailure applyToOne( 360 ::mlir::transform::TransformRewriter &rewriter, 361 ::mlir::Operation *target, 362 ::mlir::transform::ApplyToEachResultList &results, 363 ::mlir::transform::TransformState &state); 364 }]; 365} 366 367def TakeAssumedBranchOp : Op<Transform_Dialect, "scf.take_assumed_branch", [ 368 DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, 369 TransformOpInterface, TransformEachOpTrait]> { 370 let description = [{ 371 Given an scf.if conditional, inject user-defined information that it is 372 always safe to execute only the if or else branch. 373 374 This is achieved by just replacing the scf.if by the content of one of its 375 branches. 376 377 This is particularly useful for user-controlled rewriting of conditionals 378 that exist solely to guard against out-of-bounds behavior. 379 380 At the moment, no assume or assert operation is emitted as it is not always 381 desirable. In the future, this may be controlled by a dedicated attribute. 382 383 #### Return modes 384 385 The transform only consumes its operand and does not produce any result. 386 The transform definitely fails if `take_else_branch` is specified and the 387 `else` region is empty. 388 }]; 389 let arguments = (ins TransformHandleTypeInterface:$target, 390 OptionalAttr<UnitAttr>:$take_else_branch); 391 let results = (outs); 392 393 let assemblyFormat = [{ 394 $target 395 (`take_else_branch` $take_else_branch^)? 396 attr-dict 397 `:` functional-type(operands, results) 398 }]; 399 400 let extraClassDeclaration = [{ 401 ::mlir::DiagnosedSilenceableFailure applyToOne( 402 ::mlir::transform::TransformRewriter &rewriter, 403 ::mlir::scf::IfOp ifOp, 404 ::mlir::transform::ApplyToEachResultList &results, 405 ::mlir::transform::TransformState &state); 406 }]; 407} 408 409def LoopFuseSiblingOp : Op<Transform_Dialect, "loop.fuse_sibling", 410 [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface, 411 DeclareOpInterfaceMethods<TransformOpInterface>]> { 412 let summary = "Fuse a loop into another loop, assuming the fusion is legal."; 413 414 let description = [{ 415 Fuses the `target` loop into the `source` loop assuming they are 416 independent of each other. In the fused loop, the arguments, body and 417 results of `target` are placed _before_ those of `source`. 418 419 For fusion of two `scf.for` loops, the bounds and step size must match. For 420 fusion of two `scf.forall` loops, the bounds and the mapping must match. 421 Otherwise a silencable failure is produced. 422 423 The `target` and `source` handles must refer to exactly one operation, 424 otherwise a definite failure is produced. It is the responsibility of the 425 user to ensure that the `target` and `source` loops are independent of each 426 other -- this op will only perform rudimentary legality checks. 427 428 #### Return modes 429 430 This operation consumes the `target` and `source` handles and produces the 431 `fused_loop` handle, which points to the fused loop. 432 }]; 433 434 let arguments = (ins TransformHandleTypeInterface:$target, 435 TransformHandleTypeInterface:$source); 436 let results = (outs TransformHandleTypeInterface:$fused_loop); 437 let assemblyFormat = "$target `into` $source attr-dict " 438 " `:` functional-type(operands, results)"; 439} 440 441#endif // SCF_TRANSFORM_OPS 442