Skip to content

Commit 3e497a1

Browse files
committed
[MLIR] Update/fix memref region computation for affine.parallel ops
When the affine.parallel op was introduced, affine utilities weren't extended to handle it. Extending these is straightforward and natural given that addAffineParallelOpDomain has also been added. Update/complete memref region compute to account for affine.parallel ops. Handle failure cleanly. Add and expose utilities missing for affine.parallel to be consistent with affine.for. All of these allow various affine passes to work with a combination of affine.parallel and affine.for ops. Differential Revision: https://reviews.llvm.org/D145669
1 parent 7ada7bb commit 3e497a1

File tree

7 files changed

+73
-42
lines changed

7 files changed

+73
-42
lines changed

mlir/include/mlir/Dialect/Affine/Analysis/Utils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class Value;
3939
/// from the outermost 'affine.for' operation to the innermost one.
4040
void getAffineForIVs(Operation &op, SmallVectorImpl<AffineForOp> *loops);
4141

42+
/// Populates 'ivs' with IVs of the surrounding affine.for and affine.parallel
43+
/// ops ordered from the outermost one to the innermost.
44+
void getAffineIVs(Operation &op, SmallVectorImpl<Value> &ivs);
45+
4246
/// Populates 'ops' with affine operations enclosing `op` ordered from outermost
4347
/// to innermost. affine.for, affine.if, or affine.parallel ops comprise such
4448
/// surrounding affine ops.

mlir/include/mlir/Dialect/Affine/IR/AffineOps.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,21 @@ namespace mlir {
441441
/// AffineForOp.
442442
bool isAffineForInductionVar(Value val);
443443

444+
/// Returns true if `val` is the induction variable of an AffineParallelOp.
445+
bool isAffineParallelInductionVar(Value val);
446+
447+
/// Returns true if the provided value is the induction variable of an
448+
/// AffineForOp or AffineParallelOp.
449+
bool isAffineInductionVar(Value val);
450+
444451
/// Returns the loop parent of an induction variable. If the provided value is
445452
/// not an induction variable, then return nullptr.
446453
AffineForOp getForInductionVarOwner(Value val);
447454

455+
/// Returns true if the provided value is among the induction variables of an
456+
/// AffineParallelOp.
457+
AffineParallelOp getAffineParallelInductionVarOwner(Value val);
458+
448459
/// Extracts the induction variables from a list of AffineForOps and places them
449460
/// in the output argument `ivs`.
450461
void extractForInductionVars(ArrayRef<AffineForOp> forInsts,

mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,6 @@ static LogicalResult getOpIndexSet(Operation *op,
287287
return getIndexSet(ops, indexSet);
288288
}
289289

290-
/// Returns true if `val` is an induction of an affine.parallel op.
291-
static bool isAffineParallelInductionVar(Value val) {
292-
auto ivArg = val.dyn_cast<BlockArgument>();
293-
if (!ivArg)
294-
return false;
295-
return isa<AffineParallelOp>(ivArg.getOwner()->getParentOp());
296-
}
297-
298290
// Returns the number of outer loop common to 'src/dstDomain'.
299291
// Loops common to 'src/dst' domains are added to 'commonLoops' if non-null.
300292
static unsigned

mlir/lib/Dialect/Affine/Analysis/Utils.cpp

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -454,19 +454,17 @@ LogicalResult MemRefRegion::compute(Operation *op, unsigned loopDepth,
454454
unsigned rank = access.getRank();
455455

456456
LLVM_DEBUG(llvm::dbgs() << "MemRefRegion::compute: " << *op
457-
<< "depth: " << loopDepth << "\n";);
457+
<< "\ndepth: " << loopDepth << "\n";);
458458

459459
// 0-d memrefs.
460460
if (rank == 0) {
461-
SmallVector<AffineForOp, 4> ivs;
462-
getAffineForIVs(*op, &ivs);
461+
SmallVector<Value, 4> ivs;
462+
getAffineIVs(*op, ivs);
463463
assert(loopDepth <= ivs.size() && "invalid 'loopDepth'");
464464
// The first 'loopDepth' IVs are symbols for this region.
465465
ivs.resize(loopDepth);
466-
SmallVector<Value, 4> regionSymbols;
467-
extractForInductionVars(ivs, &regionSymbols);
468466
// A 0-d memref has a 0-d region.
469-
cst.reset(rank, loopDepth, /*numLocals=*/0, regionSymbols);
467+
cst.reset(rank, loopDepth, /*numLocals=*/0, ivs);
470468
return success();
471469
}
472470

@@ -503,23 +501,24 @@ LogicalResult MemRefRegion::compute(Operation *op, unsigned loopDepth,
503501
// Add inequalities for loop lower/upper bounds.
504502
for (unsigned i = 0; i < numDims + numSymbols; ++i) {
505503
auto operand = operands[i];
506-
if (auto loop = getForInductionVarOwner(operand)) {
504+
if (auto affineFor = getForInductionVarOwner(operand)) {
507505
// Note that cst can now have more dimensions than accessMap if the
508506
// bounds expressions involve outer loops or other symbols.
509507
// TODO: rewrite this to use getInstIndexSet; this way
510508
// conditionals will be handled when the latter supports it.
511-
if (failed(cst.addAffineForOpDomain(loop)))
509+
if (failed(cst.addAffineForOpDomain(affineFor)))
512510
return failure();
513-
} else {
514-
// Has to be a valid symbol.
515-
auto symbol = operand;
516-
assert(isValidSymbol(symbol));
511+
} else if (auto parallelOp = getAffineParallelInductionVarOwner(operand)) {
512+
if (failed(cst.addAffineParallelOpDomain(parallelOp)))
513+
return failure();
514+
} else if (isValidSymbol(operand)) {
517515
// Check if the symbol is a constant.
518-
if (auto *op = symbol.getDefiningOp()) {
519-
if (auto constOp = dyn_cast<arith::ConstantIndexOp>(op)) {
520-
cst.addBound(FlatAffineValueConstraints::EQ, symbol, constOp.value());
521-
}
522-
}
516+
Value symbol = operand;
517+
if (auto constVal = getConstantIntValue(symbol))
518+
cst.addBound(FlatAffineValueConstraints::EQ, symbol, constVal.value());
519+
} else {
520+
LLVM_DEBUG(llvm::dbgs() << "unknown affine dimensional value");
521+
return failure();
523522
}
524523
}
525524

@@ -552,16 +551,14 @@ LogicalResult MemRefRegion::compute(Operation *op, unsigned loopDepth,
552551

553552
// Eliminate any loop IVs other than the outermost 'loopDepth' IVs, on which
554553
// this memref region is symbolic.
555-
SmallVector<AffineForOp, 4> enclosingIVs;
556-
getAffineForIVs(*op, &enclosingIVs);
554+
SmallVector<Value, 4> enclosingIVs;
555+
getAffineIVs(*op, enclosingIVs);
557556
assert(loopDepth <= enclosingIVs.size() && "invalid loop depth");
558557
enclosingIVs.resize(loopDepth);
559558
SmallVector<Value, 4> vars;
560559
cst.getValues(cst.getNumDimVars(), cst.getNumDimAndSymbolVars(), &vars);
561-
for (auto var : vars) {
562-
AffineForOp iv;
563-
if ((iv = getForInductionVarOwner(var)) &&
564-
!llvm::is_contained(enclosingIVs, iv)) {
560+
for (Value var : vars) {
561+
if ((isAffineInductionVar(var)) && !llvm::is_contained(enclosingIVs, var)) {
565562
cst.projectOut(var);
566563
}
567564
}
@@ -1264,21 +1261,19 @@ bool MemRefAccess::operator==(const MemRefAccess &rhs) const {
12641261
[](AffineExpr e) { return e == 0; });
12651262
}
12661263

1267-
/// Populates 'loops' with IVs of the surrounding affine.for and affine.parallel
1268-
/// ops ordered from the outermost one to the innermost.
1269-
static void getAffineIVs(Operation &op, SmallVectorImpl<Value> &loops) {
1264+
void mlir::getAffineIVs(Operation &op, SmallVectorImpl<Value> &ivs) {
12701265
auto *currOp = op.getParentOp();
12711266
AffineForOp currAffineForOp;
1272-
// Traverse up the hierarchy collecting all 'affine.for' operation while
1273-
// skipping over 'affine.if' operations.
1267+
// Traverse up the hierarchy collecting all 'affine.for' and affine.parallel
1268+
// operation while skipping over 'affine.if' operations.
12741269
while (currOp) {
12751270
if (AffineForOp currAffineForOp = dyn_cast<AffineForOp>(currOp))
1276-
loops.push_back(currAffineForOp.getInductionVar());
1271+
ivs.push_back(currAffineForOp.getInductionVar());
12771272
else if (auto parOp = dyn_cast<AffineParallelOp>(currOp))
1278-
llvm::append_range(loops, parOp.getIVs());
1273+
llvm::append_range(ivs, parOp.getIVs());
12791274
currOp = currOp->getParentOp();
12801275
}
1281-
std::reverse(loops.begin(), loops.end());
1276+
std::reverse(ivs.begin(), ivs.end());
12821277
}
12831278

12841279
/// Returns the number of surrounding loops common to 'loopsA' and 'loopsB',

mlir/lib/Dialect/Affine/IR/AffineOps.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,8 +2504,14 @@ bool mlir::isAffineForInductionVar(Value val) {
25042504
return getForInductionVarOwner(val) != AffineForOp();
25052505
}
25062506

2507-
/// Returns the loop parent of an induction variable. If the provided value is
2508-
/// not an induction variable, then return nullptr.
2507+
bool mlir::isAffineParallelInductionVar(Value val) {
2508+
return getAffineParallelInductionVarOwner(val) != nullptr;
2509+
}
2510+
2511+
bool mlir::isAffineInductionVar(Value val) {
2512+
return isAffineForInductionVar(val) || isAffineParallelInductionVar(val);
2513+
}
2514+
25092515
AffineForOp mlir::getForInductionVarOwner(Value val) {
25102516
auto ivArg = val.dyn_cast<BlockArgument>();
25112517
if (!ivArg || !ivArg.getOwner())
@@ -2517,6 +2523,17 @@ AffineForOp mlir::getForInductionVarOwner(Value val) {
25172523
return AffineForOp();
25182524
}
25192525

2526+
AffineParallelOp mlir::getAffineParallelInductionVarOwner(Value val) {
2527+
auto ivArg = val.dyn_cast<BlockArgument>();
2528+
if (!ivArg || !ivArg.getOwner())
2529+
return nullptr;
2530+
Operation *containingOp = ivArg.getOwner()->getParentOp();
2531+
auto parallelOp = dyn_cast<AffineParallelOp>(containingOp);
2532+
if (parallelOp && llvm::is_contained(parallelOp.getIVs(), val))
2533+
return parallelOp;
2534+
return nullptr;
2535+
}
2536+
25202537
/// Extracts the induction variables from a list of AffineForOps and returns
25212538
/// them.
25222539
void mlir::extractForInductionVars(ArrayRef<AffineForOp> forInsts,

mlir/test/Transforms/memref-bound-check.mlir

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,14 @@ func.func @zero_d_memref() {
293293
}
294294
return
295295
}
296+
297+
// CHECK-LABEL: func @affine_parallel
298+
func.func @affine_parallel(%M: memref<2048x2048xf64>) {
299+
affine.parallel (%i) = (0) to (3000) {
300+
affine.for %j = 0 to 2048 {
301+
affine.load %M[%i, %j] : memref<2048x2048xf64>
302+
// expected-error@above {{'affine.load' op memref out of upper bound access along dimension #1}}
303+
}
304+
}
305+
return
306+
}

mlir/test/lib/Dialect/Affine/TestAffineDataCopy.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ void TestAffineDataCopy::runOnOperation() {
6060
// Gather all AffineForOps by loop depth.
6161
std::vector<SmallVector<AffineForOp, 2>> depthToLoops;
6262
gatherLoops(getOperation(), depthToLoops);
63-
assert(!depthToLoops.empty() && "Loop nest not found");
63+
if (depthToLoops.empty())
64+
return;
6465

6566
// Only support tests with a single loop nest and a single innermost loop
6667
// for now.

0 commit comments

Comments
 (0)