1//===- AffineOps.td - Affine operation definitions ---------*- 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// Defines MLIR affine operations. 10// 11//===----------------------------------------------------------------------===// 12 13#ifndef AFFINE_OPS 14#define AFFINE_OPS 15 16include "mlir/Dialect/StandardOps/IR/StandardOpsBase.td" 17include "mlir/Dialect/Affine/IR/AffineMemoryOpInterfaces.td" 18include "mlir/Interfaces/ControlFlowInterfaces.td" 19include "mlir/Interfaces/LoopLikeInterface.td" 20include "mlir/Interfaces/SideEffectInterfaces.td" 21 22def Affine_Dialect : Dialect { 23 let name = "affine"; 24 let cppNamespace = ""; 25 let hasConstantMaterializer = 1; 26} 27 28// Base class for Affine dialect ops. 29class Affine_Op<string mnemonic, list<OpTrait> traits = []> : 30 Op<Affine_Dialect, mnemonic, traits> { 31 // For every affine op, there needs to be a: 32 // * void print(OpAsmPrinter &p, ${C++ class of Op} op) 33 // * LogicalResult verify(${C++ class of Op} op) 34 // * ParseResult parse${C++ class of Op}(OpAsmParser &parser, 35 // OperationState &result) 36 // functions. 37 let printer = [{ return ::print(p, *this); }]; 38 let verifier = [{ return ::verify(*this); }]; 39 let parser = [{ return ::parse$cppClass(parser, result); }]; 40} 41 42// Require regions to have affine.yield. 43def ImplicitAffineTerminator 44 : SingleBlockImplicitTerminator<"AffineYieldOp">; 45 46def AffineApplyOp : Affine_Op<"apply", [NoSideEffect]> { 47 let summary = "affine apply operation"; 48 let description = [{ 49 The affine.apply operation applies an [affine mapping](#affine-expressions) 50 to a list of SSA values, yielding a single SSA value. The number of 51 dimension and symbol arguments to `affine.apply` must be equal to the 52 respective number of dimensional and symbolic inputs to the affine mapping; 53 the affine mapping has to be one-dimensional, and so the `affine.apply` 54 operation always returns one value. The input operands and result must all 55 have ‘index’ type. 56 57 Example: 58 59 ```mlir 60 #map10 = affine_map<(d0, d1) -> (d0 floordiv 8 + d1 floordiv 128)> 61 ... 62 %1 = affine.apply #map10 (%s, %t) 63 64 // Inline example. 65 %2 = affine.apply affine_map<(i)[s0] -> (i+s0)> (%42)[%n] 66 ``` 67 }]; 68 let arguments = (ins AffineMapAttr:$map, Variadic<Index>:$mapOperands); 69 let results = (outs Index); 70 71 // TODO: The auto-generated builders should check to see if the return type 72 // has a constant builder. That way we wouldn't need to explicitly specify the 73 // result types here. 74 let builders = [ 75 OpBuilderDAG<(ins "AffineMap":$map, "ValueRange":$mapOperands), 76 [{ 77 build($_builder, $_state, $_builder.getIndexType(), map, mapOperands); 78 }]> 79 ]; 80 81 let extraClassDeclaration = [{ 82 /// Returns the affine map to be applied by this operation. 83 AffineMap getAffineMap() { return map(); } 84 85 /// Returns the affine value map computed from this operation. 86 AffineValueMap getAffineValueMap(); 87 88 /// Returns true if the result of this operation can be used as dimension id 89 /// in the region of the closest surrounding op with trait AffineScope. 90 bool isValidDim(); 91 92 /// Returns true if the result of this operation can be used as dimension id 93 /// within 'region', i.e., for all its uses with `region`. 94 bool isValidDim(Region *region); 95 96 /// Returns true if the result of this operation is a symbol in the region 97 /// of the closest surrounding op that has the trait AffineScope. 98 bool isValidSymbol(); 99 100 /// Returns true if the result of this operation is a symbol for all its 101 /// uses in `region`. 102 bool isValidSymbol(Region *region); 103 104 operand_range getMapOperands() { return getOperands(); } 105 }]; 106 107 let hasCanonicalizer = 1; 108 let hasFolder = 1; 109} 110 111def AffineForOp : Affine_Op<"for", 112 [ImplicitAffineTerminator, RecursiveSideEffects, 113 DeclareOpInterfaceMethods<LoopLikeOpInterface>]> { 114 let summary = "for operation"; 115 let description = [{ 116 Syntax: 117 118 ``` 119 operation ::= `affine.for` ssa-id `=` lower-bound `to` upper-bound 120 (`step` integer-literal)? `{` op* `}` 121 122 lower-bound ::= `max`? affine-map-attribute dim-and-symbol-use-list | shorthand-bound 123 upper-bound ::= `min`? affine-map-attribute dim-and-symbol-use-list | shorthand-bound 124 shorthand-bound ::= ssa-id | `-`? integer-literal 125 ``` 126 127 The `affine.for` operation represents an affine loop nest. It has one region 128 containing its body. This region must contain one block that terminates with 129 [`affine.yield`](#affineyield-affineyieldop). *Note:* when 130 `affine.for` is printed in custom format, the terminator is omitted. The 131 block has one argument of [`index`](../LangRef.md#index-type) type that 132 represents the induction variable of the loop. 133 134 The `affine.for` operation executes its body a number of times iterating 135 from a lower bound to an upper bound by a stride. The stride, represented by 136 `step`, is a positive constant integer which defaults to "1" if not present. 137 The lower and upper bounds specify a half-open range: the range includes the 138 lower bound but does not include the upper bound. 139 140 The lower and upper bounds of a `affine.for` operation are represented as an 141 application of an affine mapping to a list of SSA values passed to the map. 142 The [same restrictions](#restrictions-on-dimensions-and-symbols) hold for 143 these SSA values as for all bindings of SSA values to dimensions and 144 symbols. 145 146 The affine mappings for the bounds may return multiple results, in which 147 case the `max`/`min` keywords are required (for the lower/upper bound 148 respectively), and the bound is the maximum/minimum of the returned values. 149 There is no semantic ambiguity, but MLIR syntax requires the use of these 150 keywords to make things more obvious to human readers. 151 152 Many upper and lower bounds are simple, so MLIR accepts two custom form 153 syntaxes: the form that accepts a single 'ssa-id' (e.g. `%N`) is shorthand 154 for applying that SSA value to a function that maps a single symbol to 155 itself, e.g., `()[s]->(s)()[%N]`. The integer literal form (e.g. `-42`) is 156 shorthand for a nullary mapping function that returns the constant value 157 (e.g. `()->(-42)()`). 158 159 Example showing reverse iteration of the inner loop: 160 161 ```mlir 162 #map57 = affine_map<(d0)[s0] -> (s0 - d0 - 1)> 163 164 func @simple_example(%A: memref<?x?xf32>, %B: memref<?x?xf32>) { 165 %N = dim %A, 0 : memref<?x?xf32> 166 affine.for %i = 0 to %N step 1 { 167 affine.for %j = 0 to %N { // implicitly steps by 1 168 %0 = affine.apply #map57(%j)[%N] 169 %tmp = call @F1(%A, %i, %0) : (memref<?x?xf32>, index, index)->(f32) 170 call @F2(%tmp, %B, %i, %0) : (f32, memref<?x?xf32>, index, index)->() 171 } 172 } 173 return 174 } 175 ``` 176 `affine.for` can also operate on loop-carried variables and return the final 177 values after loop termination. The initial values of the variables are 178 passed as additional SSA operands to the "affine.for" following the 2 loop 179 control values lower bound, upper bound. The operation region has equivalent 180 arguments for each variable representing the value of the variable at the 181 current iteration. 182 183 The region must terminate with an `affine.yield` that passes all the current 184 iteration variables to the next iteration, or to the `affine.for` result, if 185 at the last iteration. 186 187 `affine.for` results hold the final values after the last iteration. 188 For example, to sum-reduce a memref: 189 190 ```mlir 191 func @reduce(%buffer: memref<1024xf32>) -> (f32) { 192 // Initial sum set to 0. 193 %sum_0 = constant 0.0 : f32 194 // iter_args binds initial values to the loop's region arguments. 195 %sum = affine.for %i = 0 to 10 step 2 196 iter_args(%sum_iter = %sum_0) -> (f32) { 197 %t = affine.load %buffer[%i] : memref<1024xf32> 198 %sum_next = addf %sum_iter, %t : f32 199 // Yield current iteration sum to next iteration %sum_iter or to %sum 200 // if final iteration. 201 affine.yield %sum_next : f32 202 } 203 return %sum : f32 204 } 205 ``` 206 If the `affine.for` defines any values, a yield terminator must be 207 explicitly present. The number and types of the "affine.for" results must 208 match the initial values in the `iter_args` binding and the yield operands. 209 }]; 210 let arguments = (ins Variadic<AnyType>); 211 let results = (outs Variadic<AnyType>:$results); 212 let regions = (region SizedRegion<1>:$region); 213 214 let skipDefaultBuilders = 1; 215 let builders = [ 216 OpBuilderDAG<(ins "int64_t":$lowerBound, "int64_t":$upperBound, 217 CArg<"int64_t", "1">:$step, CArg<"ValueRange", "llvm::None">:$iterArgs, 218 CArg<"function_ref<void(OpBuilder &, Location, Value, ValueRange)>", 219 "nullptr">:$bodyBuilder)>, 220 OpBuilderDAG<(ins "ValueRange":$lbOperands, "AffineMap":$lbMap, 221 "ValueRange":$ubOperands, "AffineMap":$ubMap, CArg<"int64_t", "1">:$step, 222 CArg<"ValueRange", "llvm::None">:$iterArgs, 223 CArg<"function_ref<void(OpBuilder &, Location, Value, ValueRange)>", 224 "nullptr">:$bodyBuilder)> 225 ]; 226 227 let extraClassDeclaration = [{ 228 /// Defining the function type we use for building the body of affine.for. 229 using BodyBuilderFn = 230 function_ref<void(OpBuilder &, Location, Value, ValueRange)>; 231 232 static StringRef getStepAttrName() { return "step"; } 233 static StringRef getLowerBoundAttrName() { return "lower_bound"; } 234 static StringRef getUpperBoundAttrName() { return "upper_bound"; } 235 236 Value getInductionVar() { return getBody()->getArgument(0); } 237 Block::BlockArgListType getRegionIterArgs() { 238 return getBody()->getArguments().drop_front(); 239 } 240 Operation::operand_range getIterOperands() { 241 return getOperands().drop_front(getNumControlOperands()); 242 } 243 244 // TODO: provide iterators for the lower and upper bound operands 245 // if the current access via getLowerBound(), getUpperBound() is too slow. 246 247 /// Returns operands for the lower bound map. 248 operand_range getLowerBoundOperands(); 249 250 /// Returns operands for the upper bound map. 251 operand_range getUpperBoundOperands(); 252 253 /// Returns information about the lower bound as a single object. 254 AffineBound getLowerBound(); 255 256 /// Returns information about the upper bound as a single object. 257 AffineBound getUpperBound(); 258 259 /// Returns loop step. 260 int64_t getStep() { 261 return (*this)->getAttr(getStepAttrName()).cast<IntegerAttr>().getInt(); 262 } 263 264 /// Returns affine map for the lower bound. 265 AffineMap getLowerBoundMap() { return getLowerBoundMapAttr().getValue(); } 266 AffineMapAttr getLowerBoundMapAttr() { 267 return (*this)->getAttr(getLowerBoundAttrName()).cast<AffineMapAttr>(); 268 } 269 /// Returns affine map for the upper bound. The upper bound is exclusive. 270 AffineMap getUpperBoundMap() { return getUpperBoundMapAttr().getValue(); } 271 AffineMapAttr getUpperBoundMapAttr() { 272 return (*this)->getAttr(getUpperBoundAttrName()).cast<AffineMapAttr>(); 273 } 274 275 /// Set lower bound. The new bound must have the same number of operands as 276 /// the current bound map. Otherwise, 'replaceForLowerBound' should be used. 277 void setLowerBound(ValueRange operands, AffineMap map); 278 /// Set upper bound. The new bound must not have more operands than the 279 /// current bound map. Otherwise, 'replaceForUpperBound' should be used. 280 void setUpperBound(ValueRange operands, AffineMap map); 281 282 /// Set the lower bound map without changing operands. 283 void setLowerBoundMap(AffineMap map); 284 285 /// Set the upper bound map without changing operands. 286 void setUpperBoundMap(AffineMap map); 287 288 /// Set loop step. 289 void setStep(int64_t step) { 290 assert(step > 0 && "step has to be a positive integer constant"); 291 auto *context = getLowerBoundMap().getContext(); 292 (*this)->setAttr(Identifier::get(getStepAttrName(), context), 293 IntegerAttr::get(IndexType::get(context), step)); 294 } 295 296 /// Returns number of region arguments for loop-carried values. 297 unsigned getNumRegionIterArgs() { 298 return getBody()->getNumArguments() - 1; 299 } 300 301 /// Number of operands controlling the loop: lb and ub. 302 unsigned getNumControlOperands() { return getOperation()->getNumOperands() - getNumIterOperands(); } 303 304 /// Get the number of loop-carried values. 305 unsigned getNumIterOperands(); 306 307 /// Returns true if the lower bound is constant. 308 bool hasConstantLowerBound(); 309 /// Returns true if the upper bound is constant. 310 bool hasConstantUpperBound(); 311 /// Returns true if both bounds are constant. 312 bool hasConstantBounds() { 313 return hasConstantLowerBound() && hasConstantUpperBound(); 314 } 315 /// Returns the value of the constant lower bound. 316 /// Fails assertion if the bound is non-constant. 317 int64_t getConstantLowerBound(); 318 /// Returns the value of the constant upper bound. The upper bound is 319 /// exclusive. Fails assertion if the bound is non-constant. 320 int64_t getConstantUpperBound(); 321 /// Sets the lower bound to the given constant value. 322 void setConstantLowerBound(int64_t value); 323 /// Sets the upper bound to the given constant value. 324 void setConstantUpperBound(int64_t value); 325 326 /// Returns true if both the lower and upper bound have the same operand 327 /// lists (same operands in the same order). 328 bool matchingBoundOperandList(); 329 }]; 330 331 let hasCanonicalizer = 1; 332 let hasFolder = 1; 333} 334 335def AffineIfOp : Affine_Op<"if", 336 [ImplicitAffineTerminator, RecursiveSideEffects, 337 NoRegionArguments]> { 338 let summary = "if-then-else operation"; 339 let description = [{ 340 Syntax: 341 342 ``` 343 operation ::= `affine.if` if-op-cond `{` op* `}` (`else` `{` op* `}`)? 344 if-op-cond ::= integer-set-attr dim-and-symbol-use-list 345 ``` 346 347 The `affine.if` operation restricts execution to a subset of the loop 348 iteration space defined by an integer set (a conjunction of affine 349 constraints). A single `affine.if` may end with an optional `else` clause. 350 351 The condition of the `affine.if` is represented by an 352 [integer set](#integer-sets) (a conjunction of affine constraints), 353 and the SSA values bound to the dimensions and symbols in the integer set. 354 The [same restrictions](#restrictions-on-dimensions-and-symbols) hold for 355 these SSA values as for all bindings of SSA values to dimensions and 356 symbols. 357 358 The `affine.if` operation contains two regions for the "then" and "else" 359 clauses. `affine.if` may return results that are defined in its regions. 360 The values defined are determined by which execution path is taken. Each 361 region of the `affine.if` must contain a single block with no arguments, 362 and be terminated by `affine.yield`. If `affine.if` defines no values, 363 the `affine.yield` can be left out, and will be inserted implicitly. 364 Otherwise, it must be explicit. If no values are defined, the else block 365 may be empty (i.e. contain no blocks). 366 367 Example: 368 369 ```mlir 370 #set = affine_set<(d0, d1)[s0]: (d0 - 10 >= 0, s0 - d0 - 9 >= 0, 371 d1 - 10 >= 0, s0 - d1 - 9 >= 0)> 372 func @reduced_domain_example(%A, %X, %N) : (memref<10xi32>, i32, i32) { 373 affine.for %i = 0 to %N { 374 affine.for %j = 0 to %N { 375 %0 = affine.apply #map42(%j) 376 %tmp = call @S1(%X, %i, %0) 377 affine.if #set(%i, %j)[%N] { 378 %1 = affine.apply #map43(%i, %j) 379 call @S2(%tmp, %A, %i, %1) 380 } 381 } 382 } 383 return 384 } 385 ``` 386 387 Example with an explicit yield (initialization with edge padding): 388 389 ```mlir 390 #interior = affine_set<(i, j) : (i - 1 >= 0, j - 1 >= 0, 10 - i >= 0, 10 - j >= 0)> (%i, %j) 391 func @pad_edges(%I : memref<10x10xf32>) -> (memref<12x12xf32) { 392 %O = alloc memref<12x12xf32> 393 affine.parallel (%i, %j) = (0, 0) to (12, 12) { 394 %1 = affine.if #interior (%i, %j) { 395 %2 = load %I[%i - 1, %j - 1] : memref<10x10xf32> 396 affine.yield %2 397 } else { 398 %2 = constant 0.0 : f32 399 affine.yield %2 : f32 400 } 401 affine.store %1, %O[%i, %j] : memref<12x12xf32> 402 } 403 return %O 404 } 405 ``` 406 }]; 407 let arguments = (ins Variadic<AnyType>); 408 let results = (outs Variadic<AnyType>:$results); 409 let regions = (region SizedRegion<1>:$thenRegion, AnyRegion:$elseRegion); 410 411 let skipDefaultBuilders = 1; 412 413 let builders = [ 414 OpBuilderDAG<(ins "IntegerSet":$set, "ValueRange":$args, 415 "bool":$withElseRegion)>, 416 OpBuilderDAG<(ins "TypeRange":$resultTypes, "IntegerSet":$set, 417 "ValueRange":$args, "bool":$withElseRegion)>, 418 ]; 419 420 let extraClassDeclaration = [{ 421 static StringRef getConditionAttrName() { return "condition"; } 422 423 IntegerSet getIntegerSet(); 424 void setIntegerSet(IntegerSet newSet); 425 426 /// Sets the integer set with its operands. 427 void setConditional(IntegerSet set, ValueRange operands); 428 429 /// Returns true if an else block exists. 430 bool hasElse() { return !elseRegion().empty(); } 431 432 Block *getThenBlock() { 433 assert(!thenRegion().empty() && "Unexpected empty 'then' region."); 434 return &thenRegion().front(); 435 } 436 437 Block *getElseBlock() { 438 assert(hasElse() && "Empty 'else' region."); 439 return &elseRegion().front(); 440 } 441 442 OpBuilder getThenBodyBuilder() { 443 assert(!thenRegion().empty() && "Unexpected empty 'then' region."); 444 Block &body = thenRegion().front(); 445 return OpBuilder(&body, std::prev(body.end())); 446 } 447 OpBuilder getElseBodyBuilder() { 448 assert(hasElse() && "No 'else' block"); 449 Block &body = elseRegion().front(); 450 return OpBuilder(&body, std::prev(body.end())); 451 } 452 }]; 453 454 let hasCanonicalizer = 1; 455 let hasFolder = 1; 456} 457 458class AffineLoadOpBase<string mnemonic, list<OpTrait> traits = []> : 459 Affine_Op<mnemonic, !listconcat(traits, 460 [DeclareOpInterfaceMethods<AffineReadOpInterface>, 461 MemRefsNormalizable])> { 462 let arguments = (ins Arg<AnyMemRef, "the reference to load from", 463 [MemRead]>:$memref, 464 Variadic<Index>:$indices); 465 466 code extraClassDeclarationBase = [{ 467 /// Returns the operand index of the memref. 468 unsigned getMemRefOperandIndex() { return 0; } 469 470 void setMemRef(Value value) { setOperand(getMemRefOperandIndex(), value); } 471 472 /// Returns the affine map used to index the memref for this operation. 473 AffineMapAttr getAffineMapAttr() { 474 return (*this)->getAttr(getMapAttrName()).cast<AffineMapAttr>(); 475 } 476 477 static StringRef getMapAttrName() { return "map"; } 478 }]; 479} 480 481def AffineLoadOp : AffineLoadOpBase<"load"> { 482 let summary = "affine load operation"; 483 let description = [{ 484 The "affine.load" op reads an element from a memref, where the index 485 for each memref dimension is an affine expression of loop induction 486 variables and symbols. The output of 'affine.load' is a new value with the 487 same type as the elements of the memref. An affine expression of loop IVs 488 and symbols must be specified for each dimension of the memref. The keyword 489 'symbol' can be used to indicate SSA identifiers which are symbolic. 490 491 Example 1: 492 493 ```mlir 494 %1 = affine.load %0[%i0 + 3, %i1 + 7] : memref<100x100xf32> 495 ``` 496 497 Example 2: Uses 'symbol' keyword for symbols '%n' and '%m'. 498 499 ```mlir 500 %1 = affine.load %0[%i0 + symbol(%n), %i1 + symbol(%m)] : memref<100x100xf32> 501 ``` 502 }]; 503 504 let results = (outs AnyType:$result); 505 506 let builders = [ 507 /// Builds an affine load op with the specified map and operands. 508 OpBuilderDAG<(ins "AffineMap":$map, "ValueRange":$operands)>, 509 /// Builds an affine load op with an identity map and operands. 510 OpBuilderDAG<(ins "Value":$memref, CArg<"ValueRange", "{}">:$indices)>, 511 /// Builds an affine load op with the specified map and its operands. 512 OpBuilderDAG<(ins "Value":$memref, "AffineMap":$map, 513 "ValueRange":$mapOperands)> 514 ]; 515 516 let extraClassDeclaration = extraClassDeclarationBase; 517 518 let hasCanonicalizer = 1; 519 let hasFolder = 1; 520} 521 522class AffineMinMaxOpBase<string mnemonic, list<OpTrait> traits = []> : 523 Op<Affine_Dialect, mnemonic, traits> { 524 let arguments = (ins AffineMapAttr:$map, Variadic<Index>:$operands); 525 let results = (outs Index); 526 527 let builders = [ 528 OpBuilderDAG<(ins "AffineMap":$affineMap, "ValueRange":$mapOperands), 529 [{ 530 build($_builder, $_state, $_builder.getIndexType(), affineMap, mapOperands); 531 }]> 532 ]; 533 534 let extraClassDeclaration = [{ 535 static StringRef getMapAttrName() { return "map"; } 536 AffineMap getAffineMap() { return map(); } 537 ValueRange getMapOperands() { return operands(); } 538 ValueRange getDimOperands() { 539 return OperandRange{operands().begin(), 540 operands().begin() + map().getNumDims()}; 541 } 542 ValueRange getSymbolOperands() { 543 return OperandRange{operands().begin() + map().getNumDims(), 544 operands().end()}; 545 } 546 }]; 547 let verifier = [{ return ::verifyAffineMinMaxOp(*this); }]; 548 let printer = [{ return ::printAffineMinMaxOp(p, *this); }]; 549 let parser = [{ return ::parseAffineMinMaxOp<$cppClass>(parser, result); }]; 550 let hasFolder = 1; 551 let hasCanonicalizer = 1; 552} 553 554def AffineMinOp : AffineMinMaxOpBase<"min", [NoSideEffect]> { 555 let summary = "min operation"; 556 let description = [{ 557 Syntax: 558 559 ``` 560 operation ::= ssa-id `=` `affine.min` affine-map-attribute dim-and-symbol-use-list 561 ``` 562 563 The `affine.min` operation applies an [affine mapping](#affine-expressions) 564 to a list of SSA values, and returns the minimum value of all result 565 expressions. The number of dimension and symbol arguments to `affine.min` 566 must be equal to the respective number of dimensional and symbolic inputs to 567 the affine mapping; the `affine.min` operation always returns one value. The 568 input operands and result must all have 'index' type. 569 570 Example: 571 572 ```mlir 573 %0 = affine.min affine_map<(d0)[s0] -> (1000, d0 + 512, s0)> (%arg0)[%arg1] 574 ``` 575 }]; 576} 577 578def AffineMaxOp : AffineMinMaxOpBase<"max", [NoSideEffect]> { 579 let summary = "max operation"; 580 let description = [{ 581 The "max" operation computes the maximum value result from a multi-result 582 affine map. 583 584 Example: 585 586 ```mlir 587 %0 = affine.max (d0) -> (1000, d0 + 512) (%i0) : index 588 ``` 589 }]; 590} 591 592def AffineParallelOp : Affine_Op<"parallel", 593 [ImplicitAffineTerminator, RecursiveSideEffects, 594 DeclareOpInterfaceMethods<LoopLikeOpInterface>]> { 595 let summary = "multi-index parallel band operation"; 596 let description = [{ 597 The "affine.parallel" operation represents a hyper-rectangular affine 598 parallel band, defining multiple SSA values for its induction variables. It 599 has one region capturing the parallel band body. The induction variables are 600 represented as arguments of this region. These SSA values always have type 601 index, which is the size of the machine word. The strides, represented by 602 steps, are positive constant integers which defaults to "1" if not present. 603 The lower and upper bounds specify a half-open range: the range includes the 604 lower bound but does not include the upper bound. The body region must 605 contain exactly one block that terminates with "affine.yield". 606 607 The lower and upper bounds of a parallel operation are represented as an 608 application of an affine mapping to a list of SSA values passed to the map. 609 The same restrictions hold for these SSA values as for all bindings of SSA 610 values to dimensions and symbols. 611 612 Each value yielded by affine.yield will be accumulated/reduced via one of 613 the reduction methods defined in the AtomicRMWKind enum. The order of 614 reduction is unspecified, and lowering may produce any valid ordering. 615 Loops with a 0 trip count will produce as a result the identity value 616 associated with each reduction (i.e. 0.0 for addf, 1.0 for mulf). Assign 617 reductions for loops with a trip count != 1 produces undefined results. 618 619 Note: Calling AffineParallelOp::build will create the required region and 620 block, and insert the required terminator if it is trivial (i.e. no values 621 are yielded). Parsing will also create the required region, block, and 622 terminator, even when they are missing from the textual representation. 623 624 Example (3x3 valid convolution): 625 626 ```mlir 627 func @conv_2d(%D : memref<100x100xf32>, %K : memref<3x3xf32>) -> (memref<98x98xf32>) { 628 %O = alloc memref<98x98xf32> 629 affine.parallel (%x, %y) = (0, 0) to (98, 98) { 630 %0 = affine.parallel (%kx, %ky) = (0, 0) to (2, 2) reduce ("addf") { 631 %1 = affine.load %D[%x + %kx, %y + %ky] : memref<100x100xf32> 632 %2 = affine.load %K[%kx, %ky] : memref<3x3xf32> 633 %3 = mulf %1, %2 : f32 634 affine.yield %3 : f32 635 } 636 affine.store %0, O[%x, %y] : memref<98x98xf32> 637 } 638 return %O 639 } 640 ``` 641 }]; 642 643 let arguments = (ins 644 TypedArrayAttrBase<AtomicRMWKindAttr, "Reduction ops">:$reductions, 645 AffineMapAttr:$lowerBoundsMap, 646 AffineMapAttr:$upperBoundsMap, 647 I64ArrayAttr:$steps, 648 Variadic<Index>:$mapOperands); 649 let results = (outs Variadic<AnyType>:$results); 650 let regions = (region SizedRegion<1>:$region); 651 652 let builders = [ 653 OpBuilderDAG<(ins "TypeRange":$resultTypes, 654 "ArrayRef<AtomicRMWKind>":$reductions, "ArrayRef<int64_t>":$ranges)>, 655 OpBuilderDAG<(ins "TypeRange":$resultTypes, 656 "ArrayRef<AtomicRMWKind>":$reductions, "AffineMap":$lbMap, 657 "ValueRange":$lbArgs, "AffineMap":$ubMap, "ValueRange":$ubArgs)>, 658 OpBuilderDAG<(ins "TypeRange":$resultTypes, 659 "ArrayRef<AtomicRMWKind>":$reductions, "AffineMap":$lbMap, 660 "ValueRange":$lbArgs, "AffineMap":$ubMap, "ValueRange":$ubArgs, 661 "ArrayRef<int64_t>":$steps)> 662 ]; 663 664 let extraClassDeclaration = [{ 665 /// Get the number of dimensions. 666 unsigned getNumDims(); 667 668 AffineValueMap getRangesValueMap(); 669 670 /// Get ranges as constants, may fail in dynamic case. 671 Optional<SmallVector<int64_t, 8>> getConstantRanges(); 672 673 Block *getBody(); 674 OpBuilder getBodyBuilder(); 675 MutableArrayRef<BlockArgument> getIVs() { 676 return getBody()->getArguments(); 677 } 678 679 operand_range getLowerBoundsOperands(); 680 AffineValueMap getLowerBoundsValueMap(); 681 void setLowerBounds(ValueRange operands, AffineMap map); 682 void setLowerBoundsMap(AffineMap map); 683 684 operand_range getUpperBoundsOperands(); 685 AffineValueMap getUpperBoundsValueMap(); 686 void setUpperBounds(ValueRange operands, AffineMap map); 687 void setUpperBoundsMap(AffineMap map); 688 689 SmallVector<int64_t, 8> getSteps(); 690 void setSteps(ArrayRef<int64_t> newSteps); 691 692 static StringRef getReductionsAttrName() { return "reductions"; } 693 static StringRef getLowerBoundsMapAttrName() { return "lowerBoundsMap"; } 694 static StringRef getUpperBoundsMapAttrName() { return "upperBoundsMap"; } 695 static StringRef getStepsAttrName() { return "steps"; } 696 }]; 697 698 let hasFolder = 1; 699} 700 701def AffinePrefetchOp : Affine_Op<"prefetch"> { 702 let summary = "affine prefetch operation"; 703 let description = [{ 704 The "affine.prefetch" op prefetches data from a memref location described 705 with an affine subscript similar to affine.load, and has three attributes: 706 a read/write specifier, a locality hint, and a cache type specifier as shown 707 below: 708 709 ```mlir 710 affine.prefetch %0[%i, %j + 5], read, locality<3>, data : memref<400x400xi32> 711 ``` 712 713 The read/write specifier is either 'read' or 'write', the locality hint 714 specifier ranges from locality<0> (no locality) to locality<3> (extremely 715 local keep in cache). The cache type specifier is either 'data' or 'instr' 716 and specifies whether the prefetch is performed on data cache or on 717 instruction cache. 718 }]; 719 720 let arguments = (ins AnyMemRef:$memref, Variadic<Index>:$indices, 721 BoolAttr:$isWrite, 722 Confined<I32Attr, [IntMinValue<0>, 723 IntMaxValue<3>]>:$localityHint, 724 BoolAttr:$isDataCache); 725 726 let builders = [ 727 OpBuilderDAG<(ins "Value":$memref, "AffineMap":$map, 728 "ArrayRef<Value>":$mapOperands, "bool":$isWrite, "unsigned":$localityHint, 729 "bool":$isDataCache), 730 [{ 731 assert(map.getNumInputs() == mapOperands.size() 732 && "inconsistent index info"); 733 auto localityHintAttr = $_builder.getI32IntegerAttr(localityHint); 734 auto isWriteAttr = $_builder.getBoolAttr(isWrite); 735 auto isDataCacheAttr = $_builder.getBoolAttr(isDataCache); 736 $_state.addOperands(memref); 737 $_state.addAttribute(getMapAttrName(), AffineMapAttr::get(map)); 738 $_state.addOperands(mapOperands); 739 $_state.addAttribute(getLocalityHintAttrName(), localityHintAttr); 740 $_state.addAttribute(getIsWriteAttrName(), isWriteAttr); 741 $_state.addAttribute(getIsDataCacheAttrName(), isDataCacheAttr); 742 }]>]; 743 744 let extraClassDeclaration = [{ 745 MemRefType getMemRefType() { 746 return memref().getType().cast<MemRefType>(); 747 } 748 749 /// Returns the affine map used to index the memref for this operation. 750 AffineMap getAffineMap() { return getAffineMapAttr().getValue(); } 751 AffineMapAttr getAffineMapAttr() { 752 return (*this)->getAttr(getMapAttrName()).cast<AffineMapAttr>(); 753 } 754 755 /// Returns the AffineMapAttr associated with 'memref'. 756 NamedAttribute getAffineMapAttrForMemRef(Value mref) { 757 assert(mref == memref()); 758 return {Identifier::get(getMapAttrName(), getContext()), 759 getAffineMapAttr()}; 760 } 761 762 /// Get affine map operands. 763 operand_range getMapOperands() { 764 return {operand_begin() + 1, operand_end()}; 765 } 766 767 static StringRef getMapAttrName() { return "map"; } 768 static StringRef getLocalityHintAttrName() { return "localityHint"; } 769 static StringRef getIsWriteAttrName() { return "isWrite"; } 770 static StringRef getIsDataCacheAttrName() { return "isDataCache"; } 771 }]; 772 773 let hasCanonicalizer = 1; 774 let hasFolder = 1; 775} 776 777class AffineStoreOpBase<string mnemonic, list<OpTrait> traits = []> : 778 Affine_Op<mnemonic, !listconcat(traits, 779 [DeclareOpInterfaceMethods<AffineWriteOpInterface>, 780 MemRefsNormalizable])> { 781 code extraClassDeclarationBase = [{ 782 /// Returns the operand index of the value to be stored. 783 unsigned getStoredValOperandIndex() { return 0; } 784 785 /// Returns the operand index of the memref. 786 unsigned getMemRefOperandIndex() { return 1; } 787 788 void setMemRef(Value value) { setOperand(getMemRefOperandIndex(), value); } 789 790 /// Returns the affine map used to index the memref for this operation. 791 AffineMapAttr getAffineMapAttr() { 792 return (*this)->getAttr(getMapAttrName()).cast<AffineMapAttr>(); 793 } 794 795 /// Returns the AffineMapAttr associated with 'memref'. 796 NamedAttribute getAffineMapAttrForMemRef(Value memref) { 797 assert(memref == getMemRef()); 798 return {Identifier::get(getMapAttrName(), getContext()), 799 getAffineMapAttr()}; 800 } 801 802 static StringRef getMapAttrName() { return "map"; } 803 }]; 804} 805 806def AffineStoreOp : AffineStoreOpBase<"store"> { 807 let summary = "affine store operation"; 808 let description = [{ 809 The "affine.store" op writes an element to a memref, where the index 810 for each memref dimension is an affine expression of loop induction 811 variables and symbols. The 'affine.store' op stores a new value which is the 812 same type as the elements of the memref. An affine expression of loop IVs 813 and symbols must be specified for each dimension of the memref. The keyword 814 'symbol' can be used to indicate SSA identifiers which are symbolic. 815 816 Example 1: 817 818 ```mlir 819 affine.store %v0, %0[%i0 + 3, %i1 + 7] : memref<100x100xf32> 820 ``` 821 822 Example 2: Uses 'symbol' keyword for symbols '%n' and '%m'. 823 824 ```mlir 825 affine.store %v0, %0[%i0 + symbol(%n), %i1 + symbol(%m)] : memref<100x100xf32> 826 ``` 827 }]; 828 let arguments = (ins AnyType:$value, 829 Arg<AnyMemRef, "the reference to store to", 830 [MemWrite]>:$memref, 831 Variadic<Index>:$indices); 832 833 let skipDefaultBuilders = 1; 834 let builders = [ 835 OpBuilderDAG<(ins "Value":$valueToStore, "Value":$memref, 836 "ValueRange":$indices)>, 837 OpBuilderDAG<(ins "Value":$valueToStore, "Value":$memref, "AffineMap":$map, 838 "ValueRange":$mapOperands)> 839 ]; 840 841 let extraClassDeclaration = extraClassDeclarationBase; 842 843 let hasCanonicalizer = 1; 844 let hasFolder = 1; 845} 846 847def AffineYieldOp : Affine_Op<"yield", [NoSideEffect, Terminator, ReturnLike]> { 848 let summary = "Yield values to parent operation"; 849 let description = [{ 850 "affine.yield" yields zero or more SSA values from an affine op region and 851 terminates the region. The semantics of how the values yielded are used 852 is defined by the parent operation. 853 If "affine.yield" has any operands, the operands must match the parent 854 operation's results. 855 If the parent operation defines no values, then the "affine.yield" may be 856 left out in the custom syntax and the builders will insert one implicitly. 857 Otherwise, it has to be present in the syntax to indicate which values are 858 yielded. 859 ``` 860 }]; 861 862 let arguments = (ins Variadic<AnyType>:$operands); 863 864 let builders = [ 865 OpBuilderDAG<(ins), [{ build($_builder, $_state, llvm::None); }]> 866 ]; 867 868 let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; 869} 870 871def AffineVectorLoadOp : AffineLoadOpBase<"vector_load"> { 872 let summary = "affine vector load operation"; 873 let description = [{ 874 The "affine.vector_load" is the vector counterpart of 875 [affine.load](#affineload-operation). It reads a slice from a 876 [MemRef](../LangRef.md#memref-type), supplied as its first operand, 877 into a [vector](../LangRef.md#vector-type) of the same base elemental type. 878 The index for each memref dimension is an affine expression of loop induction 879 variables and symbols. These indices determine the start position of the read 880 within the memref. The shape of the return vector type determines the shape of 881 the slice read from the memref. This slice is contiguous along the respective 882 dimensions of the shape. Strided vector loads will be supported in the future. 883 An affine expression of loop IVs and symbols must be specified for each 884 dimension of the memref. The keyword 'symbol' can be used to indicate SSA 885 identifiers which are symbolic. 886 887 Example 1: 8-wide f32 vector load. 888 889 ```mlir 890 %1 = affine.vector_load %0[%i0 + 3, %i1 + 7] : memref<100x100xf32>, vector<8xf32> 891 ``` 892 893 Example 2: 4-wide f32 vector load. Uses 'symbol' keyword for symbols '%n' and '%m'. 894 895 ```mlir 896 %1 = affine.vector_load %0[%i0 + symbol(%n), %i1 + symbol(%m)] : memref<100x100xf32>, vector<4xf32> 897 ``` 898 899 Example 3: 2-dim f32 vector load. 900 901 ```mlir 902 %1 = affine.vector_load %0[%i0, %i1] : memref<100x100xf32>, vector<2x8xf32> 903 ``` 904 905 TODOs: 906 * Add support for strided vector loads. 907 * Consider adding a permutation map to permute the slice that is read from memory 908 (see [vector.transfer_read](../Vector/#vectortransfer_read-vectortransferreadop)). 909 }]; 910 911 let results = (outs AnyVector:$result); 912 913 let builders = [ 914 /// Builds an affine vector load op with the specified map and operands. 915 OpBuilderDAG<(ins "VectorType":$resultType, "AffineMap":$map, 916 "ValueRange":$operands)>, 917 /// Builds an affine vector load op with an identity map and operands. 918 OpBuilderDAG<(ins "VectorType":$resultType, "Value":$memref, 919 CArg<"ValueRange", "{}">:$indices)>, 920 /// Builds an affine vector load op with the specified map and its operands. 921 OpBuilderDAG<(ins "VectorType":$resultType, "Value":$memref, 922 "AffineMap":$map, "ValueRange":$mapOperands)> 923 ]; 924 925 let extraClassDeclaration = extraClassDeclarationBase # [{ 926 VectorType getVectorType() { 927 return result().getType().cast<VectorType>(); 928 } 929 }]; 930} 931 932def AffineVectorStoreOp : AffineStoreOpBase<"vector_store"> { 933 let summary = "affine vector store operation"; 934 let description = [{ 935 The "affine.vector_store" is the vector counterpart of 936 [affine.store](#affinestore-affinestoreop). It writes a 937 [vector](../LangRef.md#vector-type), supplied as its first operand, 938 into a slice within a [MemRef](../LangRef.md#memref-type) of the same base 939 elemental type, supplied as its second operand. 940 The index for each memref dimension is an affine expression of loop 941 induction variables and symbols. These indices determine the start position 942 of the write within the memref. The shape of th input vector determines the 943 shape of the slice written to the memref. This slice is contiguous along the 944 respective dimensions of the shape. Strided vector stores will be supported 945 in the future. 946 An affine expression of loop IVs and symbols must be specified for each 947 dimension of the memref. The keyword 'symbol' can be used to indicate SSA 948 identifiers which are symbolic. 949 950 Example 1: 8-wide f32 vector store. 951 952 ```mlir 953 affine.vector_store %v0, %0[%i0 + 3, %i1 + 7] : memref<100x100xf32>, vector<8xf32> 954 ``` 955 956 Example 2: 4-wide f32 vector store. Uses 'symbol' keyword for symbols '%n' and '%m'. 957 958 ```mlir 959 affine.vector_store %v0, %0[%i0 + symbol(%n), %i1 + symbol(%m)] : memref<100x100xf32>, vector<4xf32> 960 ``` 961 962 Example 3: 2-dim f32 vector store. 963 964 ```mlir 965 affine.vector_store %v0, %0[%i0, %i1] : memref<100x100xf32>, vector<2x8xf32> 966 ``` 967 968 TODOs: 969 * Add support for strided vector stores. 970 * Consider adding a permutation map to permute the slice that is written to memory 971 (see [vector.transfer_write](../Vector/#vectortransfer_write-vectortransferwriteop)). 972 }]; 973 974 let arguments = (ins AnyVector:$value, 975 Arg<AnyMemRef, "the reference to store to", 976 [MemWrite]>:$memref, 977 Variadic<Index>:$indices); 978 979 let skipDefaultBuilders = 1; 980 let builders = [ 981 OpBuilderDAG<(ins "Value":$valueToStore, "Value":$memref, 982 "ValueRange":$indices)>, 983 OpBuilderDAG<(ins "Value":$valueToStore, "Value":$memref, "AffineMap":$map, 984 "ValueRange":$mapOperands)> 985 ]; 986 987 let extraClassDeclaration = extraClassDeclarationBase # [{ 988 VectorType getVectorType() { 989 return value().getType().cast<VectorType>(); 990 } 991 }]; 992} 993 994#endif // AFFINE_OPS 995