1 //===- Utils.cpp ---- Misc utilities for analysis -------------------------===// 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 implements miscellaneous analysis routines for non-loop IR 10 // structures. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "mlir/Dialect/Affine/Analysis/Utils.h" 15 #include "mlir/Analysis/Presburger/PresburgerSet.h" 16 #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h" 17 #include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h" 18 #include "mlir/Dialect/Affine/IR/AffineOps.h" 19 #include "mlir/Dialect/Affine/IR/AffineValueMap.h" 20 #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" 21 #include "mlir/Dialect/StandardOps/IR/Ops.h" 22 #include "mlir/IR/IntegerSet.h" 23 #include "llvm/ADT/SmallPtrSet.h" 24 #include "llvm/Support/Debug.h" 25 #include "llvm/Support/raw_ostream.h" 26 27 #define DEBUG_TYPE "analysis-utils" 28 29 using namespace mlir; 30 using namespace presburger; 31 32 using llvm::SmallDenseMap; 33 34 /// Populates 'loops' with IVs of the loops surrounding 'op' ordered from 35 /// the outermost 'affine.for' operation to the innermost one. 36 void mlir::getLoopIVs(Operation &op, SmallVectorImpl<AffineForOp> *loops) { 37 auto *currOp = op.getParentOp(); 38 AffineForOp currAffineForOp; 39 // Traverse up the hierarchy collecting all 'affine.for' operation while 40 // skipping over 'affine.if' operations. 41 while (currOp) { 42 if (AffineForOp currAffineForOp = dyn_cast<AffineForOp>(currOp)) 43 loops->push_back(currAffineForOp); 44 currOp = currOp->getParentOp(); 45 } 46 std::reverse(loops->begin(), loops->end()); 47 } 48 49 /// Populates 'ops' with IVs of the loops surrounding `op`, along with 50 /// `affine.if` operations interleaved between these loops, ordered from the 51 /// outermost `affine.for` operation to the innermost one. 52 void mlir::getEnclosingAffineForAndIfOps(Operation &op, 53 SmallVectorImpl<Operation *> *ops) { 54 ops->clear(); 55 Operation *currOp = op.getParentOp(); 56 57 // Traverse up the hierarchy collecting all `affine.for` and `affine.if` 58 // operations. 59 while (currOp) { 60 if (isa<AffineIfOp, AffineForOp>(currOp)) 61 ops->push_back(currOp); 62 currOp = currOp->getParentOp(); 63 } 64 std::reverse(ops->begin(), ops->end()); 65 } 66 67 // Populates 'cst' with FlatAffineValueConstraints which represent original 68 // domain of the loop bounds that define 'ivs'. 69 LogicalResult 70 ComputationSliceState::getSourceAsConstraints(FlatAffineValueConstraints &cst) { 71 assert(!ivs.empty() && "Cannot have a slice without its IVs"); 72 cst.reset(/*numDims=*/ivs.size(), /*numSymbols=*/0, /*numLocals=*/0, ivs); 73 for (Value iv : ivs) { 74 AffineForOp loop = getForInductionVarOwner(iv); 75 assert(loop && "Expected affine for"); 76 if (failed(cst.addAffineForOpDomain(loop))) 77 return failure(); 78 } 79 return success(); 80 } 81 82 // Populates 'cst' with FlatAffineValueConstraints which represent slice bounds. 83 LogicalResult 84 ComputationSliceState::getAsConstraints(FlatAffineValueConstraints *cst) { 85 assert(!lbOperands.empty()); 86 // Adds src 'ivs' as dimension identifiers in 'cst'. 87 unsigned numDims = ivs.size(); 88 // Adds operands (dst ivs and symbols) as symbols in 'cst'. 89 unsigned numSymbols = lbOperands[0].size(); 90 91 SmallVector<Value, 4> values(ivs); 92 // Append 'ivs' then 'operands' to 'values'. 93 values.append(lbOperands[0].begin(), lbOperands[0].end()); 94 cst->reset(numDims, numSymbols, 0, values); 95 96 // Add loop bound constraints for values which are loop IVs of the destination 97 // of fusion and equality constraints for symbols which are constants. 98 for (unsigned i = numDims, end = values.size(); i < end; ++i) { 99 Value value = values[i]; 100 assert(cst->containsId(value) && "value expected to be present"); 101 if (isValidSymbol(value)) { 102 // Check if the symbol is a constant. 103 if (auto cOp = value.getDefiningOp<arith::ConstantIndexOp>()) 104 cst->addBound(FlatAffineConstraints::EQ, value, cOp.value()); 105 } else if (auto loop = getForInductionVarOwner(value)) { 106 if (failed(cst->addAffineForOpDomain(loop))) 107 return failure(); 108 } 109 } 110 111 // Add slices bounds on 'ivs' using maps 'lbs'/'ubs' with 'lbOperands[0]' 112 LogicalResult ret = cst->addSliceBounds(ivs, lbs, ubs, lbOperands[0]); 113 assert(succeeded(ret) && 114 "should not fail as we never have semi-affine slice maps"); 115 (void)ret; 116 return success(); 117 } 118 119 // Clears state bounds and operand state. 120 void ComputationSliceState::clearBounds() { 121 lbs.clear(); 122 ubs.clear(); 123 lbOperands.clear(); 124 ubOperands.clear(); 125 } 126 127 void ComputationSliceState::dump() const { 128 llvm::errs() << "\tIVs:\n"; 129 for (Value iv : ivs) 130 llvm::errs() << "\t\t" << iv << "\n"; 131 132 llvm::errs() << "\tLBs:\n"; 133 for (auto &en : llvm::enumerate(lbs)) { 134 llvm::errs() << "\t\t" << en.value() << "\n"; 135 llvm::errs() << "\t\tOperands:\n"; 136 for (Value lbOp : lbOperands[en.index()]) 137 llvm::errs() << "\t\t\t" << lbOp << "\n"; 138 } 139 140 llvm::errs() << "\tUBs:\n"; 141 for (auto &en : llvm::enumerate(ubs)) { 142 llvm::errs() << "\t\t" << en.value() << "\n"; 143 llvm::errs() << "\t\tOperands:\n"; 144 for (Value ubOp : ubOperands[en.index()]) 145 llvm::errs() << "\t\t\t" << ubOp << "\n"; 146 } 147 } 148 149 /// Fast check to determine if the computation slice is maximal. Returns true if 150 /// each slice dimension maps to an existing dst dimension and both the src 151 /// and the dst loops for those dimensions have the same bounds. Returns false 152 /// if both the src and the dst loops don't have the same bounds. Returns 153 /// llvm::None if none of the above can be proven. 154 Optional<bool> ComputationSliceState::isSliceMaximalFastCheck() const { 155 assert(lbs.size() == ubs.size() && !lbs.empty() && !ivs.empty() && 156 "Unexpected number of lbs, ubs and ivs in slice"); 157 158 for (unsigned i = 0, end = lbs.size(); i < end; ++i) { 159 AffineMap lbMap = lbs[i]; 160 AffineMap ubMap = ubs[i]; 161 162 // Check if this slice is just an equality along this dimension. 163 if (!lbMap || !ubMap || lbMap.getNumResults() != 1 || 164 ubMap.getNumResults() != 1 || 165 lbMap.getResult(0) + 1 != ubMap.getResult(0) || 166 // The condition above will be true for maps describing a single 167 // iteration (e.g., lbMap.getResult(0) = 0, ubMap.getResult(0) = 1). 168 // Make sure we skip those cases by checking that the lb result is not 169 // just a constant. 170 lbMap.getResult(0).isa<AffineConstantExpr>()) 171 return llvm::None; 172 173 // Limited support: we expect the lb result to be just a loop dimension for 174 // now. 175 AffineDimExpr result = lbMap.getResult(0).dyn_cast<AffineDimExpr>(); 176 if (!result) 177 return llvm::None; 178 179 // Retrieve dst loop bounds. 180 AffineForOp dstLoop = 181 getForInductionVarOwner(lbOperands[i][result.getPosition()]); 182 if (!dstLoop) 183 return llvm::None; 184 AffineMap dstLbMap = dstLoop.getLowerBoundMap(); 185 AffineMap dstUbMap = dstLoop.getUpperBoundMap(); 186 187 // Retrieve src loop bounds. 188 AffineForOp srcLoop = getForInductionVarOwner(ivs[i]); 189 assert(srcLoop && "Expected affine for"); 190 AffineMap srcLbMap = srcLoop.getLowerBoundMap(); 191 AffineMap srcUbMap = srcLoop.getUpperBoundMap(); 192 193 // Limited support: we expect simple src and dst loops with a single 194 // constant component per bound for now. 195 if (srcLbMap.getNumResults() != 1 || srcUbMap.getNumResults() != 1 || 196 dstLbMap.getNumResults() != 1 || dstUbMap.getNumResults() != 1) 197 return llvm::None; 198 199 AffineExpr srcLbResult = srcLbMap.getResult(0); 200 AffineExpr dstLbResult = dstLbMap.getResult(0); 201 AffineExpr srcUbResult = srcUbMap.getResult(0); 202 AffineExpr dstUbResult = dstUbMap.getResult(0); 203 if (!srcLbResult.isa<AffineConstantExpr>() || 204 !srcUbResult.isa<AffineConstantExpr>() || 205 !dstLbResult.isa<AffineConstantExpr>() || 206 !dstUbResult.isa<AffineConstantExpr>()) 207 return llvm::None; 208 209 // Check if src and dst loop bounds are the same. If not, we can guarantee 210 // that the slice is not maximal. 211 if (srcLbResult != dstLbResult || srcUbResult != dstUbResult || 212 srcLoop.getStep() != dstLoop.getStep()) 213 return false; 214 } 215 216 return true; 217 } 218 219 /// Returns true if it is deterministically verified that the original iteration 220 /// space of the slice is contained within the new iteration space that is 221 /// created after fusing 'this' slice into its destination. 222 Optional<bool> ComputationSliceState::isSliceValid() { 223 // Fast check to determine if the slice is valid. If the following conditions 224 // are verified to be true, slice is declared valid by the fast check: 225 // 1. Each slice loop is a single iteration loop bound in terms of a single 226 // destination loop IV. 227 // 2. Loop bounds of the destination loop IV (from above) and those of the 228 // source loop IV are exactly the same. 229 // If the fast check is inconclusive or false, we proceed with a more 230 // expensive analysis. 231 // TODO: Store the result of the fast check, as it might be used again in 232 // `canRemoveSrcNodeAfterFusion`. 233 Optional<bool> isValidFastCheck = isSliceMaximalFastCheck(); 234 if (isValidFastCheck.hasValue() && isValidFastCheck.getValue()) 235 return true; 236 237 // Create constraints for the source loop nest using which slice is computed. 238 FlatAffineValueConstraints srcConstraints; 239 // TODO: Store the source's domain to avoid computation at each depth. 240 if (failed(getSourceAsConstraints(srcConstraints))) { 241 LLVM_DEBUG(llvm::dbgs() << "Unable to compute source's domain\n"); 242 return llvm::None; 243 } 244 // As the set difference utility currently cannot handle symbols in its 245 // operands, validity of the slice cannot be determined. 246 if (srcConstraints.getNumSymbolIds() > 0) { 247 LLVM_DEBUG(llvm::dbgs() << "Cannot handle symbols in source domain\n"); 248 return llvm::None; 249 } 250 // TODO: Handle local ids in the source domains while using the 'projectOut' 251 // utility below. Currently, aligning is not done assuming that there will be 252 // no local ids in the source domain. 253 if (srcConstraints.getNumLocalIds() != 0) { 254 LLVM_DEBUG(llvm::dbgs() << "Cannot handle locals in source domain\n"); 255 return llvm::None; 256 } 257 258 // Create constraints for the slice loop nest that would be created if the 259 // fusion succeeds. 260 FlatAffineValueConstraints sliceConstraints; 261 if (failed(getAsConstraints(&sliceConstraints))) { 262 LLVM_DEBUG(llvm::dbgs() << "Unable to compute slice's domain\n"); 263 return llvm::None; 264 } 265 266 // Projecting out every dimension other than the 'ivs' to express slice's 267 // domain completely in terms of source's IVs. 268 sliceConstraints.projectOut(ivs.size(), 269 sliceConstraints.getNumIds() - ivs.size()); 270 271 LLVM_DEBUG(llvm::dbgs() << "Domain of the source of the slice:\n"); 272 LLVM_DEBUG(srcConstraints.dump()); 273 LLVM_DEBUG(llvm::dbgs() << "Domain of the slice if this fusion succeeds " 274 "(expressed in terms of its source's IVs):\n"); 275 LLVM_DEBUG(sliceConstraints.dump()); 276 277 // TODO: Store 'srcSet' to avoid recalculating for each depth. 278 PresburgerSet srcSet(srcConstraints); 279 PresburgerSet sliceSet(sliceConstraints); 280 PresburgerSet diffSet = sliceSet.subtract(srcSet); 281 282 if (!diffSet.isIntegerEmpty()) { 283 LLVM_DEBUG(llvm::dbgs() << "Incorrect slice\n"); 284 return false; 285 } 286 return true; 287 } 288 289 /// Returns true if the computation slice encloses all the iterations of the 290 /// sliced loop nest. Returns false if it does not. Returns llvm::None if it 291 /// cannot determine if the slice is maximal or not. 292 Optional<bool> ComputationSliceState::isMaximal() const { 293 // Fast check to determine if the computation slice is maximal. If the result 294 // is inconclusive, we proceed with a more expensive analysis. 295 Optional<bool> isMaximalFastCheck = isSliceMaximalFastCheck(); 296 if (isMaximalFastCheck.hasValue()) 297 return isMaximalFastCheck; 298 299 // Create constraints for the src loop nest being sliced. 300 FlatAffineValueConstraints srcConstraints; 301 srcConstraints.reset(/*numDims=*/ivs.size(), /*numSymbols=*/0, 302 /*numLocals=*/0, ivs); 303 for (Value iv : ivs) { 304 AffineForOp loop = getForInductionVarOwner(iv); 305 assert(loop && "Expected affine for"); 306 if (failed(srcConstraints.addAffineForOpDomain(loop))) 307 return llvm::None; 308 } 309 310 // Create constraints for the slice using the dst loop nest information. We 311 // retrieve existing dst loops from the lbOperands. 312 SmallVector<Value, 8> consumerIVs; 313 for (Value lbOp : lbOperands[0]) 314 if (getForInductionVarOwner(lbOp)) 315 consumerIVs.push_back(lbOp); 316 317 // Add empty IV Values for those new loops that are not equalities and, 318 // therefore, are not yet materialized in the IR. 319 for (int i = consumerIVs.size(), end = ivs.size(); i < end; ++i) 320 consumerIVs.push_back(Value()); 321 322 FlatAffineValueConstraints sliceConstraints; 323 sliceConstraints.reset(/*numDims=*/consumerIVs.size(), /*numSymbols=*/0, 324 /*numLocals=*/0, consumerIVs); 325 326 if (failed(sliceConstraints.addDomainFromSliceMaps(lbs, ubs, lbOperands[0]))) 327 return llvm::None; 328 329 if (srcConstraints.getNumDimIds() != sliceConstraints.getNumDimIds()) 330 // Constraint dims are different. The integer set difference can't be 331 // computed so we don't know if the slice is maximal. 332 return llvm::None; 333 334 // Compute the difference between the src loop nest and the slice integer 335 // sets. 336 PresburgerSet srcSet(srcConstraints); 337 PresburgerSet sliceSet(sliceConstraints); 338 PresburgerSet diffSet = srcSet.subtract(sliceSet); 339 return diffSet.isIntegerEmpty(); 340 } 341 342 unsigned MemRefRegion::getRank() const { 343 return memref.getType().cast<MemRefType>().getRank(); 344 } 345 346 Optional<int64_t> MemRefRegion::getConstantBoundingSizeAndShape( 347 SmallVectorImpl<int64_t> *shape, std::vector<SmallVector<int64_t, 4>> *lbs, 348 SmallVectorImpl<int64_t> *lbDivisors) const { 349 auto memRefType = memref.getType().cast<MemRefType>(); 350 unsigned rank = memRefType.getRank(); 351 if (shape) 352 shape->reserve(rank); 353 354 assert(rank == cst.getNumDimIds() && "inconsistent memref region"); 355 356 // Use a copy of the region constraints that has upper/lower bounds for each 357 // memref dimension with static size added to guard against potential 358 // over-approximation from projection or union bounding box. We may not add 359 // this on the region itself since they might just be redundant constraints 360 // that will need non-trivials means to eliminate. 361 FlatAffineConstraints cstWithShapeBounds(cst); 362 for (unsigned r = 0; r < rank; r++) { 363 cstWithShapeBounds.addBound(FlatAffineConstraints::LB, r, 0); 364 int64_t dimSize = memRefType.getDimSize(r); 365 if (ShapedType::isDynamic(dimSize)) 366 continue; 367 cstWithShapeBounds.addBound(FlatAffineConstraints::UB, r, dimSize - 1); 368 } 369 370 // Find a constant upper bound on the extent of this memref region along each 371 // dimension. 372 int64_t numElements = 1; 373 int64_t diffConstant; 374 int64_t lbDivisor; 375 for (unsigned d = 0; d < rank; d++) { 376 SmallVector<int64_t, 4> lb; 377 Optional<int64_t> diff = 378 cstWithShapeBounds.getConstantBoundOnDimSize(d, &lb, &lbDivisor); 379 if (diff.hasValue()) { 380 diffConstant = diff.getValue(); 381 assert(diffConstant >= 0 && "Dim size bound can't be negative"); 382 assert(lbDivisor > 0); 383 } else { 384 // If no constant bound is found, then it can always be bound by the 385 // memref's dim size if the latter has a constant size along this dim. 386 auto dimSize = memRefType.getDimSize(d); 387 if (dimSize == -1) 388 return None; 389 diffConstant = dimSize; 390 // Lower bound becomes 0. 391 lb.resize(cstWithShapeBounds.getNumSymbolIds() + 1, 0); 392 lbDivisor = 1; 393 } 394 numElements *= diffConstant; 395 if (lbs) { 396 lbs->push_back(lb); 397 assert(lbDivisors && "both lbs and lbDivisor or none"); 398 lbDivisors->push_back(lbDivisor); 399 } 400 if (shape) { 401 shape->push_back(diffConstant); 402 } 403 } 404 return numElements; 405 } 406 407 void MemRefRegion::getLowerAndUpperBound(unsigned pos, AffineMap &lbMap, 408 AffineMap &ubMap) const { 409 assert(pos < cst.getNumDimIds() && "invalid position"); 410 auto memRefType = memref.getType().cast<MemRefType>(); 411 unsigned rank = memRefType.getRank(); 412 413 assert(rank == cst.getNumDimIds() && "inconsistent memref region"); 414 415 auto boundPairs = cst.getLowerAndUpperBound( 416 pos, /*offset=*/0, /*num=*/rank, cst.getNumDimAndSymbolIds(), 417 /*localExprs=*/{}, memRefType.getContext()); 418 lbMap = boundPairs.first; 419 ubMap = boundPairs.second; 420 assert(lbMap && "lower bound for a region must exist"); 421 assert(ubMap && "upper bound for a region must exist"); 422 assert(lbMap.getNumInputs() == cst.getNumDimAndSymbolIds() - rank); 423 assert(ubMap.getNumInputs() == cst.getNumDimAndSymbolIds() - rank); 424 } 425 426 LogicalResult MemRefRegion::unionBoundingBox(const MemRefRegion &other) { 427 assert(memref == other.memref); 428 return cst.unionBoundingBox(*other.getConstraints()); 429 } 430 431 /// Computes the memory region accessed by this memref with the region 432 /// represented as constraints symbolic/parametric in 'loopDepth' loops 433 /// surrounding opInst and any additional Function symbols. 434 // For example, the memref region for this load operation at loopDepth = 1 will 435 // be as below: 436 // 437 // affine.for %i = 0 to 32 { 438 // affine.for %ii = %i to (d0) -> (d0 + 8) (%i) { 439 // load %A[%ii] 440 // } 441 // } 442 // 443 // region: {memref = %A, write = false, {%i <= m0 <= %i + 7} } 444 // The last field is a 2-d FlatAffineConstraints symbolic in %i. 445 // 446 // TODO: extend this to any other memref dereferencing ops 447 // (dma_start, dma_wait). 448 LogicalResult MemRefRegion::compute(Operation *op, unsigned loopDepth, 449 const ComputationSliceState *sliceState, 450 bool addMemRefDimBounds) { 451 assert((isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) && 452 "affine read/write op expected"); 453 454 MemRefAccess access(op); 455 memref = access.memref; 456 write = access.isStore(); 457 458 unsigned rank = access.getRank(); 459 460 LLVM_DEBUG(llvm::dbgs() << "MemRefRegion::compute: " << *op 461 << "depth: " << loopDepth << "\n";); 462 463 // 0-d memrefs. 464 if (rank == 0) { 465 SmallVector<AffineForOp, 4> ivs; 466 getLoopIVs(*op, &ivs); 467 assert(loopDepth <= ivs.size() && "invalid 'loopDepth'"); 468 // The first 'loopDepth' IVs are symbols for this region. 469 ivs.resize(loopDepth); 470 SmallVector<Value, 4> regionSymbols; 471 extractForInductionVars(ivs, ®ionSymbols); 472 // A 0-d memref has a 0-d region. 473 cst.reset(rank, loopDepth, /*numLocals=*/0, regionSymbols); 474 return success(); 475 } 476 477 // Build the constraints for this region. 478 AffineValueMap accessValueMap; 479 access.getAccessMap(&accessValueMap); 480 AffineMap accessMap = accessValueMap.getAffineMap(); 481 482 unsigned numDims = accessMap.getNumDims(); 483 unsigned numSymbols = accessMap.getNumSymbols(); 484 unsigned numOperands = accessValueMap.getNumOperands(); 485 // Merge operands with slice operands. 486 SmallVector<Value, 4> operands; 487 operands.resize(numOperands); 488 for (unsigned i = 0; i < numOperands; ++i) 489 operands[i] = accessValueMap.getOperand(i); 490 491 if (sliceState != nullptr) { 492 operands.reserve(operands.size() + sliceState->lbOperands[0].size()); 493 // Append slice operands to 'operands' as symbols. 494 for (auto extraOperand : sliceState->lbOperands[0]) { 495 if (!llvm::is_contained(operands, extraOperand)) { 496 operands.push_back(extraOperand); 497 numSymbols++; 498 } 499 } 500 } 501 // We'll first associate the dims and symbols of the access map to the dims 502 // and symbols resp. of cst. This will change below once cst is 503 // fully constructed out. 504 cst.reset(numDims, numSymbols, 0, operands); 505 506 // Add equality constraints. 507 // Add inequalities for loop lower/upper bounds. 508 for (unsigned i = 0; i < numDims + numSymbols; ++i) { 509 auto operand = operands[i]; 510 if (auto loop = getForInductionVarOwner(operand)) { 511 // Note that cst can now have more dimensions than accessMap if the 512 // bounds expressions involve outer loops or other symbols. 513 // TODO: rewrite this to use getInstIndexSet; this way 514 // conditionals will be handled when the latter supports it. 515 if (failed(cst.addAffineForOpDomain(loop))) 516 return failure(); 517 } else { 518 // Has to be a valid symbol. 519 auto symbol = operand; 520 assert(isValidSymbol(symbol)); 521 // Check if the symbol is a constant. 522 if (auto *op = symbol.getDefiningOp()) { 523 if (auto constOp = dyn_cast<arith::ConstantIndexOp>(op)) { 524 cst.addBound(FlatAffineConstraints::EQ, symbol, constOp.value()); 525 } 526 } 527 } 528 } 529 530 // Add lower/upper bounds on loop IVs using bounds from 'sliceState'. 531 if (sliceState != nullptr) { 532 // Add dim and symbol slice operands. 533 for (auto operand : sliceState->lbOperands[0]) { 534 cst.addInductionVarOrTerminalSymbol(operand); 535 } 536 // Add upper/lower bounds from 'sliceState' to 'cst'. 537 LogicalResult ret = 538 cst.addSliceBounds(sliceState->ivs, sliceState->lbs, sliceState->ubs, 539 sliceState->lbOperands[0]); 540 assert(succeeded(ret) && 541 "should not fail as we never have semi-affine slice maps"); 542 (void)ret; 543 } 544 545 // Add access function equalities to connect loop IVs to data dimensions. 546 if (failed(cst.composeMap(&accessValueMap))) { 547 op->emitError("getMemRefRegion: compose affine map failed"); 548 LLVM_DEBUG(accessValueMap.getAffineMap().dump()); 549 return failure(); 550 } 551 552 // Set all identifiers appearing after the first 'rank' identifiers as 553 // symbolic identifiers - so that the ones corresponding to the memref 554 // dimensions are the dimensional identifiers for the memref region. 555 cst.setDimSymbolSeparation(cst.getNumDimAndSymbolIds() - rank); 556 557 // Eliminate any loop IVs other than the outermost 'loopDepth' IVs, on which 558 // this memref region is symbolic. 559 SmallVector<AffineForOp, 4> enclosingIVs; 560 getLoopIVs(*op, &enclosingIVs); 561 assert(loopDepth <= enclosingIVs.size() && "invalid loop depth"); 562 enclosingIVs.resize(loopDepth); 563 SmallVector<Value, 4> ids; 564 cst.getValues(cst.getNumDimIds(), cst.getNumDimAndSymbolIds(), &ids); 565 for (auto id : ids) { 566 AffineForOp iv; 567 if ((iv = getForInductionVarOwner(id)) && 568 !llvm::is_contained(enclosingIVs, iv)) { 569 cst.projectOut(id); 570 } 571 } 572 573 // Project out any local variables (these would have been added for any 574 // mod/divs). 575 cst.projectOut(cst.getNumDimAndSymbolIds(), cst.getNumLocalIds()); 576 577 // Constant fold any symbolic identifiers. 578 cst.constantFoldIdRange(/*pos=*/cst.getNumDimIds(), 579 /*num=*/cst.getNumSymbolIds()); 580 581 assert(cst.getNumDimIds() == rank && "unexpected MemRefRegion format"); 582 583 // Add upper/lower bounds for each memref dimension with static size 584 // to guard against potential over-approximation from projection. 585 // TODO: Support dynamic memref dimensions. 586 if (addMemRefDimBounds) { 587 auto memRefType = memref.getType().cast<MemRefType>(); 588 for (unsigned r = 0; r < rank; r++) { 589 cst.addBound(FlatAffineConstraints::LB, /*pos=*/r, /*value=*/0); 590 if (memRefType.isDynamicDim(r)) 591 continue; 592 cst.addBound(FlatAffineConstraints::UB, /*pos=*/r, 593 memRefType.getDimSize(r) - 1); 594 } 595 } 596 cst.removeTrivialRedundancy(); 597 598 LLVM_DEBUG(llvm::dbgs() << "Memory region:\n"); 599 LLVM_DEBUG(cst.dump()); 600 return success(); 601 } 602 603 static unsigned getMemRefEltSizeInBytes(MemRefType memRefType) { 604 auto elementType = memRefType.getElementType(); 605 606 unsigned sizeInBits; 607 if (elementType.isIntOrFloat()) { 608 sizeInBits = elementType.getIntOrFloatBitWidth(); 609 } else { 610 auto vectorType = elementType.cast<VectorType>(); 611 sizeInBits = 612 vectorType.getElementTypeBitWidth() * vectorType.getNumElements(); 613 } 614 return llvm::divideCeil(sizeInBits, 8); 615 } 616 617 // Returns the size of the region. 618 Optional<int64_t> MemRefRegion::getRegionSize() { 619 auto memRefType = memref.getType().cast<MemRefType>(); 620 621 if (!memRefType.getLayout().isIdentity()) { 622 LLVM_DEBUG(llvm::dbgs() << "Non-identity layout map not yet supported\n"); 623 return false; 624 } 625 626 // Indices to use for the DmaStart op. 627 // Indices for the original memref being DMAed from/to. 628 SmallVector<Value, 4> memIndices; 629 // Indices for the faster buffer being DMAed into/from. 630 SmallVector<Value, 4> bufIndices; 631 632 // Compute the extents of the buffer. 633 Optional<int64_t> numElements = getConstantBoundingSizeAndShape(); 634 if (!numElements.hasValue()) { 635 LLVM_DEBUG(llvm::dbgs() << "Dynamic shapes not yet supported\n"); 636 return None; 637 } 638 return getMemRefEltSizeInBytes(memRefType) * numElements.getValue(); 639 } 640 641 /// Returns the size of memref data in bytes if it's statically shaped, None 642 /// otherwise. If the element of the memref has vector type, takes into account 643 /// size of the vector as well. 644 // TODO: improve/complete this when we have target data. 645 Optional<uint64_t> mlir::getMemRefSizeInBytes(MemRefType memRefType) { 646 if (!memRefType.hasStaticShape()) 647 return None; 648 auto elementType = memRefType.getElementType(); 649 if (!elementType.isIntOrFloat() && !elementType.isa<VectorType>()) 650 return None; 651 652 uint64_t sizeInBytes = getMemRefEltSizeInBytes(memRefType); 653 for (unsigned i = 0, e = memRefType.getRank(); i < e; i++) { 654 sizeInBytes = sizeInBytes * memRefType.getDimSize(i); 655 } 656 return sizeInBytes; 657 } 658 659 template <typename LoadOrStoreOp> 660 LogicalResult mlir::boundCheckLoadOrStoreOp(LoadOrStoreOp loadOrStoreOp, 661 bool emitError) { 662 static_assert(llvm::is_one_of<LoadOrStoreOp, AffineReadOpInterface, 663 AffineWriteOpInterface>::value, 664 "argument should be either a AffineReadOpInterface or a " 665 "AffineWriteOpInterface"); 666 667 Operation *op = loadOrStoreOp.getOperation(); 668 MemRefRegion region(op->getLoc()); 669 if (failed(region.compute(op, /*loopDepth=*/0, /*sliceState=*/nullptr, 670 /*addMemRefDimBounds=*/false))) 671 return success(); 672 673 LLVM_DEBUG(llvm::dbgs() << "Memory region"); 674 LLVM_DEBUG(region.getConstraints()->dump()); 675 676 bool outOfBounds = false; 677 unsigned rank = loadOrStoreOp.getMemRefType().getRank(); 678 679 // For each dimension, check for out of bounds. 680 for (unsigned r = 0; r < rank; r++) { 681 FlatAffineConstraints ucst(*region.getConstraints()); 682 683 // Intersect memory region with constraint capturing out of bounds (both out 684 // of upper and out of lower), and check if the constraint system is 685 // feasible. If it is, there is at least one point out of bounds. 686 SmallVector<int64_t, 4> ineq(rank + 1, 0); 687 int64_t dimSize = loadOrStoreOp.getMemRefType().getDimSize(r); 688 // TODO: handle dynamic dim sizes. 689 if (dimSize == -1) 690 continue; 691 692 // Check for overflow: d_i >= memref dim size. 693 ucst.addBound(FlatAffineConstraints::LB, r, dimSize); 694 outOfBounds = !ucst.isEmpty(); 695 if (outOfBounds && emitError) { 696 loadOrStoreOp.emitOpError() 697 << "memref out of upper bound access along dimension #" << (r + 1); 698 } 699 700 // Check for a negative index. 701 FlatAffineConstraints lcst(*region.getConstraints()); 702 std::fill(ineq.begin(), ineq.end(), 0); 703 // d_i <= -1; 704 lcst.addBound(FlatAffineConstraints::UB, r, -1); 705 outOfBounds = !lcst.isEmpty(); 706 if (outOfBounds && emitError) { 707 loadOrStoreOp.emitOpError() 708 << "memref out of lower bound access along dimension #" << (r + 1); 709 } 710 } 711 return failure(outOfBounds); 712 } 713 714 // Explicitly instantiate the template so that the compiler knows we need them! 715 template LogicalResult 716 mlir::boundCheckLoadOrStoreOp(AffineReadOpInterface loadOp, bool emitError); 717 template LogicalResult 718 mlir::boundCheckLoadOrStoreOp(AffineWriteOpInterface storeOp, bool emitError); 719 720 // Returns in 'positions' the Block positions of 'op' in each ancestor 721 // Block from the Block containing operation, stopping at 'limitBlock'. 722 static void findInstPosition(Operation *op, Block *limitBlock, 723 SmallVectorImpl<unsigned> *positions) { 724 Block *block = op->getBlock(); 725 while (block != limitBlock) { 726 // FIXME: This algorithm is unnecessarily O(n) and should be improved to not 727 // rely on linear scans. 728 int instPosInBlock = std::distance(block->begin(), op->getIterator()); 729 positions->push_back(instPosInBlock); 730 op = block->getParentOp(); 731 block = op->getBlock(); 732 } 733 std::reverse(positions->begin(), positions->end()); 734 } 735 736 // Returns the Operation in a possibly nested set of Blocks, where the 737 // position of the operation is represented by 'positions', which has a 738 // Block position for each level of nesting. 739 static Operation *getInstAtPosition(ArrayRef<unsigned> positions, 740 unsigned level, Block *block) { 741 unsigned i = 0; 742 for (auto &op : *block) { 743 if (i != positions[level]) { 744 ++i; 745 continue; 746 } 747 if (level == positions.size() - 1) 748 return &op; 749 if (auto childAffineForOp = dyn_cast<AffineForOp>(op)) 750 return getInstAtPosition(positions, level + 1, 751 childAffineForOp.getBody()); 752 753 for (auto ®ion : op.getRegions()) { 754 for (auto &b : region) 755 if (auto *ret = getInstAtPosition(positions, level + 1, &b)) 756 return ret; 757 } 758 return nullptr; 759 } 760 return nullptr; 761 } 762 763 // Adds loop IV bounds to 'cst' for loop IVs not found in 'ivs'. 764 static LogicalResult addMissingLoopIVBounds(SmallPtrSet<Value, 8> &ivs, 765 FlatAffineValueConstraints *cst) { 766 for (unsigned i = 0, e = cst->getNumDimIds(); i < e; ++i) { 767 auto value = cst->getValue(i); 768 if (ivs.count(value) == 0) { 769 assert(isForInductionVar(value)); 770 auto loop = getForInductionVarOwner(value); 771 if (failed(cst->addAffineForOpDomain(loop))) 772 return failure(); 773 } 774 } 775 return success(); 776 } 777 778 /// Returns the innermost common loop depth for the set of operations in 'ops'. 779 // TODO: Move this to LoopUtils. 780 unsigned mlir::getInnermostCommonLoopDepth( 781 ArrayRef<Operation *> ops, SmallVectorImpl<AffineForOp> *surroundingLoops) { 782 unsigned numOps = ops.size(); 783 assert(numOps > 0 && "Expected at least one operation"); 784 785 std::vector<SmallVector<AffineForOp, 4>> loops(numOps); 786 unsigned loopDepthLimit = std::numeric_limits<unsigned>::max(); 787 for (unsigned i = 0; i < numOps; ++i) { 788 getLoopIVs(*ops[i], &loops[i]); 789 loopDepthLimit = 790 std::min(loopDepthLimit, static_cast<unsigned>(loops[i].size())); 791 } 792 793 unsigned loopDepth = 0; 794 for (unsigned d = 0; d < loopDepthLimit; ++d) { 795 unsigned i; 796 for (i = 1; i < numOps; ++i) { 797 if (loops[i - 1][d] != loops[i][d]) 798 return loopDepth; 799 } 800 if (surroundingLoops) 801 surroundingLoops->push_back(loops[i - 1][d]); 802 ++loopDepth; 803 } 804 return loopDepth; 805 } 806 807 /// Computes in 'sliceUnion' the union of all slice bounds computed at 808 /// 'loopDepth' between all dependent pairs of ops in 'opsA' and 'opsB', and 809 /// then verifies if it is valid. Returns 'SliceComputationResult::Success' if 810 /// union was computed correctly, an appropriate failure otherwise. 811 SliceComputationResult 812 mlir::computeSliceUnion(ArrayRef<Operation *> opsA, ArrayRef<Operation *> opsB, 813 unsigned loopDepth, unsigned numCommonLoops, 814 bool isBackwardSlice, 815 ComputationSliceState *sliceUnion) { 816 // Compute the union of slice bounds between all pairs in 'opsA' and 817 // 'opsB' in 'sliceUnionCst'. 818 FlatAffineValueConstraints sliceUnionCst; 819 assert(sliceUnionCst.getNumDimAndSymbolIds() == 0); 820 std::vector<std::pair<Operation *, Operation *>> dependentOpPairs; 821 for (auto *i : opsA) { 822 MemRefAccess srcAccess(i); 823 for (auto *j : opsB) { 824 MemRefAccess dstAccess(j); 825 if (srcAccess.memref != dstAccess.memref) 826 continue; 827 // Check if 'loopDepth' exceeds nesting depth of src/dst ops. 828 if ((!isBackwardSlice && loopDepth > getNestingDepth(i)) || 829 (isBackwardSlice && loopDepth > getNestingDepth(j))) { 830 LLVM_DEBUG(llvm::dbgs() << "Invalid loop depth\n"); 831 return SliceComputationResult::GenericFailure; 832 } 833 834 bool readReadAccesses = isa<AffineReadOpInterface>(srcAccess.opInst) && 835 isa<AffineReadOpInterface>(dstAccess.opInst); 836 FlatAffineValueConstraints dependenceConstraints; 837 // Check dependence between 'srcAccess' and 'dstAccess'. 838 DependenceResult result = checkMemrefAccessDependence( 839 srcAccess, dstAccess, /*loopDepth=*/numCommonLoops + 1, 840 &dependenceConstraints, /*dependenceComponents=*/nullptr, 841 /*allowRAR=*/readReadAccesses); 842 if (result.value == DependenceResult::Failure) { 843 LLVM_DEBUG(llvm::dbgs() << "Dependence check failed\n"); 844 return SliceComputationResult::GenericFailure; 845 } 846 if (result.value == DependenceResult::NoDependence) 847 continue; 848 dependentOpPairs.emplace_back(i, j); 849 850 // Compute slice bounds for 'srcAccess' and 'dstAccess'. 851 ComputationSliceState tmpSliceState; 852 mlir::getComputationSliceState(i, j, &dependenceConstraints, loopDepth, 853 isBackwardSlice, &tmpSliceState); 854 855 if (sliceUnionCst.getNumDimAndSymbolIds() == 0) { 856 // Initialize 'sliceUnionCst' with the bounds computed in previous step. 857 if (failed(tmpSliceState.getAsConstraints(&sliceUnionCst))) { 858 LLVM_DEBUG(llvm::dbgs() 859 << "Unable to compute slice bound constraints\n"); 860 return SliceComputationResult::GenericFailure; 861 } 862 assert(sliceUnionCst.getNumDimAndSymbolIds() > 0); 863 continue; 864 } 865 866 // Compute constraints for 'tmpSliceState' in 'tmpSliceCst'. 867 FlatAffineValueConstraints tmpSliceCst; 868 if (failed(tmpSliceState.getAsConstraints(&tmpSliceCst))) { 869 LLVM_DEBUG(llvm::dbgs() 870 << "Unable to compute slice bound constraints\n"); 871 return SliceComputationResult::GenericFailure; 872 } 873 874 // Align coordinate spaces of 'sliceUnionCst' and 'tmpSliceCst' if needed. 875 if (!sliceUnionCst.areIdsAlignedWithOther(tmpSliceCst)) { 876 877 // Pre-constraint id alignment: record loop IVs used in each constraint 878 // system. 879 SmallPtrSet<Value, 8> sliceUnionIVs; 880 for (unsigned k = 0, l = sliceUnionCst.getNumDimIds(); k < l; ++k) 881 sliceUnionIVs.insert(sliceUnionCst.getValue(k)); 882 SmallPtrSet<Value, 8> tmpSliceIVs; 883 for (unsigned k = 0, l = tmpSliceCst.getNumDimIds(); k < l; ++k) 884 tmpSliceIVs.insert(tmpSliceCst.getValue(k)); 885 886 sliceUnionCst.mergeAndAlignIdsWithOther(/*offset=*/0, &tmpSliceCst); 887 888 // Post-constraint id alignment: add loop IV bounds missing after 889 // id alignment to constraint systems. This can occur if one constraint 890 // system uses an loop IV that is not used by the other. The call 891 // to unionBoundingBox below expects constraints for each Loop IV, even 892 // if they are the unsliced full loop bounds added here. 893 if (failed(addMissingLoopIVBounds(sliceUnionIVs, &sliceUnionCst))) 894 return SliceComputationResult::GenericFailure; 895 if (failed(addMissingLoopIVBounds(tmpSliceIVs, &tmpSliceCst))) 896 return SliceComputationResult::GenericFailure; 897 } 898 // Compute union bounding box of 'sliceUnionCst' and 'tmpSliceCst'. 899 if (sliceUnionCst.getNumLocalIds() > 0 || 900 tmpSliceCst.getNumLocalIds() > 0 || 901 failed(sliceUnionCst.unionBoundingBox(tmpSliceCst))) { 902 LLVM_DEBUG(llvm::dbgs() 903 << "Unable to compute union bounding box of slice bounds\n"); 904 return SliceComputationResult::GenericFailure; 905 } 906 } 907 } 908 909 // Empty union. 910 if (sliceUnionCst.getNumDimAndSymbolIds() == 0) 911 return SliceComputationResult::GenericFailure; 912 913 // Gather loops surrounding ops from loop nest where slice will be inserted. 914 SmallVector<Operation *, 4> ops; 915 for (auto &dep : dependentOpPairs) { 916 ops.push_back(isBackwardSlice ? dep.second : dep.first); 917 } 918 SmallVector<AffineForOp, 4> surroundingLoops; 919 unsigned innermostCommonLoopDepth = 920 getInnermostCommonLoopDepth(ops, &surroundingLoops); 921 if (loopDepth > innermostCommonLoopDepth) { 922 LLVM_DEBUG(llvm::dbgs() << "Exceeds max loop depth\n"); 923 return SliceComputationResult::GenericFailure; 924 } 925 926 // Store 'numSliceLoopIVs' before converting dst loop IVs to dims. 927 unsigned numSliceLoopIVs = sliceUnionCst.getNumDimIds(); 928 929 // Convert any dst loop IVs which are symbol identifiers to dim identifiers. 930 sliceUnionCst.convertLoopIVSymbolsToDims(); 931 sliceUnion->clearBounds(); 932 sliceUnion->lbs.resize(numSliceLoopIVs, AffineMap()); 933 sliceUnion->ubs.resize(numSliceLoopIVs, AffineMap()); 934 935 // Get slice bounds from slice union constraints 'sliceUnionCst'. 936 sliceUnionCst.getSliceBounds(/*offset=*/0, numSliceLoopIVs, 937 opsA[0]->getContext(), &sliceUnion->lbs, 938 &sliceUnion->ubs); 939 940 // Add slice bound operands of union. 941 SmallVector<Value, 4> sliceBoundOperands; 942 sliceUnionCst.getValues(numSliceLoopIVs, 943 sliceUnionCst.getNumDimAndSymbolIds(), 944 &sliceBoundOperands); 945 946 // Copy src loop IVs from 'sliceUnionCst' to 'sliceUnion'. 947 sliceUnion->ivs.clear(); 948 sliceUnionCst.getValues(0, numSliceLoopIVs, &sliceUnion->ivs); 949 950 // Set loop nest insertion point to block start at 'loopDepth'. 951 sliceUnion->insertPoint = 952 isBackwardSlice 953 ? surroundingLoops[loopDepth - 1].getBody()->begin() 954 : std::prev(surroundingLoops[loopDepth - 1].getBody()->end()); 955 956 // Give each bound its own copy of 'sliceBoundOperands' for subsequent 957 // canonicalization. 958 sliceUnion->lbOperands.resize(numSliceLoopIVs, sliceBoundOperands); 959 sliceUnion->ubOperands.resize(numSliceLoopIVs, sliceBoundOperands); 960 961 // Check if the slice computed is valid. Return success only if it is verified 962 // that the slice is valid, otherwise return appropriate failure status. 963 Optional<bool> isSliceValid = sliceUnion->isSliceValid(); 964 if (!isSliceValid.hasValue()) { 965 LLVM_DEBUG(llvm::dbgs() << "Cannot determine if the slice is valid\n"); 966 return SliceComputationResult::GenericFailure; 967 } 968 if (!isSliceValid.getValue()) 969 return SliceComputationResult::IncorrectSliceFailure; 970 971 return SliceComputationResult::Success; 972 } 973 974 // TODO: extend this to handle multiple result maps. 975 static Optional<uint64_t> getConstDifference(AffineMap lbMap, AffineMap ubMap) { 976 assert(lbMap.getNumResults() == 1 && "expected single result bound map"); 977 assert(ubMap.getNumResults() == 1 && "expected single result bound map"); 978 assert(lbMap.getNumDims() == ubMap.getNumDims()); 979 assert(lbMap.getNumSymbols() == ubMap.getNumSymbols()); 980 AffineExpr lbExpr(lbMap.getResult(0)); 981 AffineExpr ubExpr(ubMap.getResult(0)); 982 auto loopSpanExpr = simplifyAffineExpr(ubExpr - lbExpr, lbMap.getNumDims(), 983 lbMap.getNumSymbols()); 984 auto cExpr = loopSpanExpr.dyn_cast<AffineConstantExpr>(); 985 if (!cExpr) 986 return None; 987 return cExpr.getValue(); 988 } 989 990 // Builds a map 'tripCountMap' from AffineForOp to constant trip count for loop 991 // nest surrounding represented by slice loop bounds in 'slice'. Returns true 992 // on success, false otherwise (if a non-constant trip count was encountered). 993 // TODO: Make this work with non-unit step loops. 994 bool mlir::buildSliceTripCountMap( 995 const ComputationSliceState &slice, 996 llvm::SmallDenseMap<Operation *, uint64_t, 8> *tripCountMap) { 997 unsigned numSrcLoopIVs = slice.ivs.size(); 998 // Populate map from AffineForOp -> trip count 999 for (unsigned i = 0; i < numSrcLoopIVs; ++i) { 1000 AffineForOp forOp = getForInductionVarOwner(slice.ivs[i]); 1001 auto *op = forOp.getOperation(); 1002 AffineMap lbMap = slice.lbs[i]; 1003 AffineMap ubMap = slice.ubs[i]; 1004 // If lower or upper bound maps are null or provide no results, it implies 1005 // that source loop was not at all sliced, and the entire loop will be a 1006 // part of the slice. 1007 if (!lbMap || lbMap.getNumResults() == 0 || !ubMap || 1008 ubMap.getNumResults() == 0) { 1009 // The iteration of src loop IV 'i' was not sliced. Use full loop bounds. 1010 if (forOp.hasConstantLowerBound() && forOp.hasConstantUpperBound()) { 1011 (*tripCountMap)[op] = 1012 forOp.getConstantUpperBound() - forOp.getConstantLowerBound(); 1013 continue; 1014 } 1015 Optional<uint64_t> maybeConstTripCount = getConstantTripCount(forOp); 1016 if (maybeConstTripCount.hasValue()) { 1017 (*tripCountMap)[op] = maybeConstTripCount.getValue(); 1018 continue; 1019 } 1020 return false; 1021 } 1022 Optional<uint64_t> tripCount = getConstDifference(lbMap, ubMap); 1023 // Slice bounds are created with a constant ub - lb difference. 1024 if (!tripCount.hasValue()) 1025 return false; 1026 (*tripCountMap)[op] = tripCount.getValue(); 1027 } 1028 return true; 1029 } 1030 1031 // Return the number of iterations in the given slice. 1032 uint64_t mlir::getSliceIterationCount( 1033 const llvm::SmallDenseMap<Operation *, uint64_t, 8> &sliceTripCountMap) { 1034 uint64_t iterCount = 1; 1035 for (const auto &count : sliceTripCountMap) { 1036 iterCount *= count.second; 1037 } 1038 return iterCount; 1039 } 1040 1041 const char *const kSliceFusionBarrierAttrName = "slice_fusion_barrier"; 1042 // Computes slice bounds by projecting out any loop IVs from 1043 // 'dependenceConstraints' at depth greater than 'loopDepth', and computes slice 1044 // bounds in 'sliceState' which represent the one loop nest's IVs in terms of 1045 // the other loop nest's IVs, symbols and constants (using 'isBackwardsSlice'). 1046 void mlir::getComputationSliceState( 1047 Operation *depSourceOp, Operation *depSinkOp, 1048 FlatAffineValueConstraints *dependenceConstraints, unsigned loopDepth, 1049 bool isBackwardSlice, ComputationSliceState *sliceState) { 1050 // Get loop nest surrounding src operation. 1051 SmallVector<AffineForOp, 4> srcLoopIVs; 1052 getLoopIVs(*depSourceOp, &srcLoopIVs); 1053 unsigned numSrcLoopIVs = srcLoopIVs.size(); 1054 1055 // Get loop nest surrounding dst operation. 1056 SmallVector<AffineForOp, 4> dstLoopIVs; 1057 getLoopIVs(*depSinkOp, &dstLoopIVs); 1058 unsigned numDstLoopIVs = dstLoopIVs.size(); 1059 1060 assert((!isBackwardSlice && loopDepth <= numSrcLoopIVs) || 1061 (isBackwardSlice && loopDepth <= numDstLoopIVs)); 1062 1063 // Project out dimensions other than those up to 'loopDepth'. 1064 unsigned pos = isBackwardSlice ? numSrcLoopIVs + loopDepth : loopDepth; 1065 unsigned num = 1066 isBackwardSlice ? numDstLoopIVs - loopDepth : numSrcLoopIVs - loopDepth; 1067 dependenceConstraints->projectOut(pos, num); 1068 1069 // Add slice loop IV values to 'sliceState'. 1070 unsigned offset = isBackwardSlice ? 0 : loopDepth; 1071 unsigned numSliceLoopIVs = isBackwardSlice ? numSrcLoopIVs : numDstLoopIVs; 1072 dependenceConstraints->getValues(offset, offset + numSliceLoopIVs, 1073 &sliceState->ivs); 1074 1075 // Set up lower/upper bound affine maps for the slice. 1076 sliceState->lbs.resize(numSliceLoopIVs, AffineMap()); 1077 sliceState->ubs.resize(numSliceLoopIVs, AffineMap()); 1078 1079 // Get bounds for slice IVs in terms of other IVs, symbols, and constants. 1080 dependenceConstraints->getSliceBounds(offset, numSliceLoopIVs, 1081 depSourceOp->getContext(), 1082 &sliceState->lbs, &sliceState->ubs); 1083 1084 // Set up bound operands for the slice's lower and upper bounds. 1085 SmallVector<Value, 4> sliceBoundOperands; 1086 unsigned numDimsAndSymbols = dependenceConstraints->getNumDimAndSymbolIds(); 1087 for (unsigned i = 0; i < numDimsAndSymbols; ++i) { 1088 if (i < offset || i >= offset + numSliceLoopIVs) { 1089 sliceBoundOperands.push_back(dependenceConstraints->getValue(i)); 1090 } 1091 } 1092 1093 // Give each bound its own copy of 'sliceBoundOperands' for subsequent 1094 // canonicalization. 1095 sliceState->lbOperands.resize(numSliceLoopIVs, sliceBoundOperands); 1096 sliceState->ubOperands.resize(numSliceLoopIVs, sliceBoundOperands); 1097 1098 // Set destination loop nest insertion point to block start at 'dstLoopDepth'. 1099 sliceState->insertPoint = 1100 isBackwardSlice ? dstLoopIVs[loopDepth - 1].getBody()->begin() 1101 : std::prev(srcLoopIVs[loopDepth - 1].getBody()->end()); 1102 1103 llvm::SmallDenseSet<Value, 8> sequentialLoops; 1104 if (isa<AffineReadOpInterface>(depSourceOp) && 1105 isa<AffineReadOpInterface>(depSinkOp)) { 1106 // For read-read access pairs, clear any slice bounds on sequential loops. 1107 // Get sequential loops in loop nest rooted at 'srcLoopIVs[0]'. 1108 getSequentialLoops(isBackwardSlice ? srcLoopIVs[0] : dstLoopIVs[0], 1109 &sequentialLoops); 1110 } 1111 auto getSliceLoop = [&](unsigned i) { 1112 return isBackwardSlice ? srcLoopIVs[i] : dstLoopIVs[i]; 1113 }; 1114 auto isInnermostInsertion = [&]() { 1115 return (isBackwardSlice ? loopDepth >= srcLoopIVs.size() 1116 : loopDepth >= dstLoopIVs.size()); 1117 }; 1118 llvm::SmallDenseMap<Operation *, uint64_t, 8> sliceTripCountMap; 1119 auto srcIsUnitSlice = [&]() { 1120 return (buildSliceTripCountMap(*sliceState, &sliceTripCountMap) && 1121 (getSliceIterationCount(sliceTripCountMap) == 1)); 1122 }; 1123 // Clear all sliced loop bounds beginning at the first sequential loop, or 1124 // first loop with a slice fusion barrier attribute.. 1125 1126 for (unsigned i = 0; i < numSliceLoopIVs; ++i) { 1127 Value iv = getSliceLoop(i).getInductionVar(); 1128 if (sequentialLoops.count(iv) == 0 && 1129 getSliceLoop(i)->getAttr(kSliceFusionBarrierAttrName) == nullptr) 1130 continue; 1131 // Skip reset of bounds of reduction loop inserted in the destination loop 1132 // that meets the following conditions: 1133 // 1. Slice is single trip count. 1134 // 2. Loop bounds of the source and destination match. 1135 // 3. Is being inserted at the innermost insertion point. 1136 Optional<bool> isMaximal = sliceState->isMaximal(); 1137 if (isLoopParallelAndContainsReduction(getSliceLoop(i)) && 1138 isInnermostInsertion() && srcIsUnitSlice() && isMaximal.hasValue() && 1139 isMaximal.getValue()) 1140 continue; 1141 for (unsigned j = i; j < numSliceLoopIVs; ++j) { 1142 sliceState->lbs[j] = AffineMap(); 1143 sliceState->ubs[j] = AffineMap(); 1144 } 1145 break; 1146 } 1147 } 1148 1149 /// Creates a computation slice of the loop nest surrounding 'srcOpInst', 1150 /// updates the slice loop bounds with any non-null bound maps specified in 1151 /// 'sliceState', and inserts this slice into the loop nest surrounding 1152 /// 'dstOpInst' at loop depth 'dstLoopDepth'. 1153 // TODO: extend the slicing utility to compute slices that 1154 // aren't necessarily a one-to-one relation b/w the source and destination. The 1155 // relation between the source and destination could be many-to-many in general. 1156 // TODO: the slice computation is incorrect in the cases 1157 // where the dependence from the source to the destination does not cover the 1158 // entire destination index set. Subtract out the dependent destination 1159 // iterations from destination index set and check for emptiness --- this is one 1160 // solution. 1161 AffineForOp 1162 mlir::insertBackwardComputationSlice(Operation *srcOpInst, Operation *dstOpInst, 1163 unsigned dstLoopDepth, 1164 ComputationSliceState *sliceState) { 1165 // Get loop nest surrounding src operation. 1166 SmallVector<AffineForOp, 4> srcLoopIVs; 1167 getLoopIVs(*srcOpInst, &srcLoopIVs); 1168 unsigned numSrcLoopIVs = srcLoopIVs.size(); 1169 1170 // Get loop nest surrounding dst operation. 1171 SmallVector<AffineForOp, 4> dstLoopIVs; 1172 getLoopIVs(*dstOpInst, &dstLoopIVs); 1173 unsigned dstLoopIVsSize = dstLoopIVs.size(); 1174 if (dstLoopDepth > dstLoopIVsSize) { 1175 dstOpInst->emitError("invalid destination loop depth"); 1176 return AffineForOp(); 1177 } 1178 1179 // Find the op block positions of 'srcOpInst' within 'srcLoopIVs'. 1180 SmallVector<unsigned, 4> positions; 1181 // TODO: This code is incorrect since srcLoopIVs can be 0-d. 1182 findInstPosition(srcOpInst, srcLoopIVs[0]->getBlock(), &positions); 1183 1184 // Clone src loop nest and insert it a the beginning of the operation block 1185 // of the loop at 'dstLoopDepth' in 'dstLoopIVs'. 1186 auto dstAffineForOp = dstLoopIVs[dstLoopDepth - 1]; 1187 OpBuilder b(dstAffineForOp.getBody(), dstAffineForOp.getBody()->begin()); 1188 auto sliceLoopNest = 1189 cast<AffineForOp>(b.clone(*srcLoopIVs[0].getOperation())); 1190 1191 Operation *sliceInst = 1192 getInstAtPosition(positions, /*level=*/0, sliceLoopNest.getBody()); 1193 // Get loop nest surrounding 'sliceInst'. 1194 SmallVector<AffineForOp, 4> sliceSurroundingLoops; 1195 getLoopIVs(*sliceInst, &sliceSurroundingLoops); 1196 1197 // Sanity check. 1198 unsigned sliceSurroundingLoopsSize = sliceSurroundingLoops.size(); 1199 (void)sliceSurroundingLoopsSize; 1200 assert(dstLoopDepth + numSrcLoopIVs >= sliceSurroundingLoopsSize); 1201 unsigned sliceLoopLimit = dstLoopDepth + numSrcLoopIVs; 1202 (void)sliceLoopLimit; 1203 assert(sliceLoopLimit >= sliceSurroundingLoopsSize); 1204 1205 // Update loop bounds for loops in 'sliceLoopNest'. 1206 for (unsigned i = 0; i < numSrcLoopIVs; ++i) { 1207 auto forOp = sliceSurroundingLoops[dstLoopDepth + i]; 1208 if (AffineMap lbMap = sliceState->lbs[i]) 1209 forOp.setLowerBound(sliceState->lbOperands[i], lbMap); 1210 if (AffineMap ubMap = sliceState->ubs[i]) 1211 forOp.setUpperBound(sliceState->ubOperands[i], ubMap); 1212 } 1213 return sliceLoopNest; 1214 } 1215 1216 // Constructs MemRefAccess populating it with the memref, its indices and 1217 // opinst from 'loadOrStoreOpInst'. 1218 MemRefAccess::MemRefAccess(Operation *loadOrStoreOpInst) { 1219 if (auto loadOp = dyn_cast<AffineReadOpInterface>(loadOrStoreOpInst)) { 1220 memref = loadOp.getMemRef(); 1221 opInst = loadOrStoreOpInst; 1222 auto loadMemrefType = loadOp.getMemRefType(); 1223 indices.reserve(loadMemrefType.getRank()); 1224 for (auto index : loadOp.getMapOperands()) { 1225 indices.push_back(index); 1226 } 1227 } else { 1228 assert(isa<AffineWriteOpInterface>(loadOrStoreOpInst) && 1229 "Affine read/write op expected"); 1230 auto storeOp = cast<AffineWriteOpInterface>(loadOrStoreOpInst); 1231 opInst = loadOrStoreOpInst; 1232 memref = storeOp.getMemRef(); 1233 auto storeMemrefType = storeOp.getMemRefType(); 1234 indices.reserve(storeMemrefType.getRank()); 1235 for (auto index : storeOp.getMapOperands()) { 1236 indices.push_back(index); 1237 } 1238 } 1239 } 1240 1241 unsigned MemRefAccess::getRank() const { 1242 return memref.getType().cast<MemRefType>().getRank(); 1243 } 1244 1245 bool MemRefAccess::isStore() const { 1246 return isa<AffineWriteOpInterface>(opInst); 1247 } 1248 1249 /// Returns the nesting depth of this statement, i.e., the number of loops 1250 /// surrounding this statement. 1251 unsigned mlir::getNestingDepth(Operation *op) { 1252 Operation *currOp = op; 1253 unsigned depth = 0; 1254 while ((currOp = currOp->getParentOp())) { 1255 if (isa<AffineForOp>(currOp)) 1256 depth++; 1257 } 1258 return depth; 1259 } 1260 1261 /// Equal if both affine accesses are provably equivalent (at compile 1262 /// time) when considering the memref, the affine maps and their respective 1263 /// operands. The equality of access functions + operands is checked by 1264 /// subtracting fully composed value maps, and then simplifying the difference 1265 /// using the expression flattener. 1266 /// TODO: this does not account for aliasing of memrefs. 1267 bool MemRefAccess::operator==(const MemRefAccess &rhs) const { 1268 if (memref != rhs.memref) 1269 return false; 1270 1271 AffineValueMap diff, thisMap, rhsMap; 1272 getAccessMap(&thisMap); 1273 rhs.getAccessMap(&rhsMap); 1274 AffineValueMap::difference(thisMap, rhsMap, &diff); 1275 return llvm::all_of(diff.getAffineMap().getResults(), 1276 [](AffineExpr e) { return e == 0; }); 1277 } 1278 1279 /// Returns the number of surrounding loops common to 'loopsA' and 'loopsB', 1280 /// where each lists loops from outer-most to inner-most in loop nest. 1281 unsigned mlir::getNumCommonSurroundingLoops(Operation &a, Operation &b) { 1282 SmallVector<AffineForOp, 4> loopsA, loopsB; 1283 getLoopIVs(a, &loopsA); 1284 getLoopIVs(b, &loopsB); 1285 1286 unsigned minNumLoops = std::min(loopsA.size(), loopsB.size()); 1287 unsigned numCommonLoops = 0; 1288 for (unsigned i = 0; i < minNumLoops; ++i) { 1289 if (loopsA[i].getOperation() != loopsB[i].getOperation()) 1290 break; 1291 ++numCommonLoops; 1292 } 1293 return numCommonLoops; 1294 } 1295 1296 static Optional<int64_t> getMemoryFootprintBytes(Block &block, 1297 Block::iterator start, 1298 Block::iterator end, 1299 int memorySpace) { 1300 SmallDenseMap<Value, std::unique_ptr<MemRefRegion>, 4> regions; 1301 1302 // Walk this 'affine.for' operation to gather all memory regions. 1303 auto result = block.walk(start, end, [&](Operation *opInst) -> WalkResult { 1304 if (!isa<AffineReadOpInterface, AffineWriteOpInterface>(opInst)) { 1305 // Neither load nor a store op. 1306 return WalkResult::advance(); 1307 } 1308 1309 // Compute the memref region symbolic in any IVs enclosing this block. 1310 auto region = std::make_unique<MemRefRegion>(opInst->getLoc()); 1311 if (failed( 1312 region->compute(opInst, 1313 /*loopDepth=*/getNestingDepth(&*block.begin())))) { 1314 return opInst->emitError("error obtaining memory region\n"); 1315 } 1316 1317 auto it = regions.find(region->memref); 1318 if (it == regions.end()) { 1319 regions[region->memref] = std::move(region); 1320 } else if (failed(it->second->unionBoundingBox(*region))) { 1321 return opInst->emitWarning( 1322 "getMemoryFootprintBytes: unable to perform a union on a memory " 1323 "region"); 1324 } 1325 return WalkResult::advance(); 1326 }); 1327 if (result.wasInterrupted()) 1328 return None; 1329 1330 int64_t totalSizeInBytes = 0; 1331 for (const auto ®ion : regions) { 1332 Optional<int64_t> size = region.second->getRegionSize(); 1333 if (!size.hasValue()) 1334 return None; 1335 totalSizeInBytes += size.getValue(); 1336 } 1337 return totalSizeInBytes; 1338 } 1339 1340 Optional<int64_t> mlir::getMemoryFootprintBytes(AffineForOp forOp, 1341 int memorySpace) { 1342 auto *forInst = forOp.getOperation(); 1343 return ::getMemoryFootprintBytes( 1344 *forInst->getBlock(), Block::iterator(forInst), 1345 std::next(Block::iterator(forInst)), memorySpace); 1346 } 1347 1348 /// Returns whether a loop is parallel and contains a reduction loop. 1349 bool mlir::isLoopParallelAndContainsReduction(AffineForOp forOp) { 1350 SmallVector<LoopReduction> reductions; 1351 if (!isLoopParallel(forOp, &reductions)) 1352 return false; 1353 return !reductions.empty(); 1354 } 1355 1356 /// Returns in 'sequentialLoops' all sequential loops in loop nest rooted 1357 /// at 'forOp'. 1358 void mlir::getSequentialLoops(AffineForOp forOp, 1359 llvm::SmallDenseSet<Value, 8> *sequentialLoops) { 1360 forOp->walk([&](Operation *op) { 1361 if (auto innerFor = dyn_cast<AffineForOp>(op)) 1362 if (!isLoopParallel(innerFor)) 1363 sequentialLoops->insert(innerFor.getInductionVar()); 1364 }); 1365 } 1366 1367 IntegerSet mlir::simplifyIntegerSet(IntegerSet set) { 1368 FlatAffineConstraints fac(set); 1369 if (fac.isEmpty()) 1370 return IntegerSet::getEmptySet(set.getNumDims(), set.getNumSymbols(), 1371 set.getContext()); 1372 fac.removeTrivialRedundancy(); 1373 1374 auto simplifiedSet = fac.getAsIntegerSet(set.getContext()); 1375 assert(simplifiedSet && "guaranteed to succeed while roundtripping"); 1376 return simplifiedSet; 1377 } 1378