Skip to content

Commit 70e99f3

Browse files
committed
[mlir] Make ViewLikeInterface Range work with attributes
While most of methods in ViewLikeInterface accept an `OpFoldResult` for the offset/size/stride that may be static, represented as `Attribute`, or dynamic, represented as `Value`, the `Range` abstraction only accepted `Values`. This can often lead to known-constant offset/size/strides being materialized into constant operations and hinder further constant propagation without explicitly running the constant folding pass. This often leads to a more complicated than necessary addressing code being emitted. Switch `Range` to use `OpFoldResult`. Code that uses `Range` currently keeps materializing the constants to minimize the effect of this change on the IR. Further commits will make use of this. Reviewed By: nicolasvasilache, mravishankar Differential Revision: https://reviews.llvm.org/D129633
1 parent 08a1b07 commit 70e99f3

File tree

11 files changed

+89
-70
lines changed

11 files changed

+89
-70
lines changed

mlir/include/mlir/Dialect/Linalg/IR/Linalg.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,6 @@ namespace linalg {
3030

3131
class LinalgOp;
3232

33-
// TOFO: allow an extra ValueRange to specify an indexing and allow
34-
// non-hyperrectangular shapes.
35-
using LoopRangeBuilder =
36-
std::function<SmallVector<Range, 4>(ImplicitLocOpBuilder)>;
37-
38-
/// Provide a very simple inference procedure to build the loop ranges from the
39-
/// op and its operands. This only works with permutation affine maps and
40-
/// patterns of the form `(m, n)[s] -> (m + n - s floordiv 2)`.
41-
/// A more advanced Tensor-Comprehension like inference is possible but has
42-
/// proven to be ambiguous in unfavorable case.
43-
/// As a consequence, we relax the default behavior very conservatively and
44-
/// provide an op-specified hook so that Linalg ops may override the behavior.
45-
LoopRangeBuilder defaultLoopRangesBuilder(LinalgOp op);
46-
4733
/// Returns the name mangled library call name to disambiguate between different
4834
/// overloads at the C level. The name mangling scheme is basic and uses MLIR
4935
/// type names:

mlir/include/mlir/Dialect/Linalg/Utils/Utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ SmallVector<Value> insertSlicesBack(OpBuilder &builder, Location loc,
208208
/// necessary.
209209
Value materializeOpFoldResult(ImplicitLocOpBuilder &builder,
210210
OpFoldResult opFoldResult);
211+
Value materializeOpFoldResult(OpBuilder &b, Location loc,
212+
OpFoldResult opFoldResult);
211213

212214
/// Creates an extract_slice/subview op for a single `valueToTile` with
213215
/// `builder`. This new operation extracts a tile of `valueToTile`, starting

mlir/include/mlir/Interfaces/ViewLikeInterface.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ namespace mlir {
2424
/// operands into a list of triples. Such a list can be more convenient to
2525
/// manipulate.
2626
struct Range {
27-
Value offset;
28-
Value size;
29-
Value stride;
27+
OpFoldResult offset;
28+
OpFoldResult size;
29+
OpFoldResult stride;
3030
};
3131

3232
class OffsetSizeAndStrideOpInterface;

mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,26 @@ static FailureOr<SmallVector<Value>> collapseGenericOpIterationDims(
13461346
genericOp, "illegal to collapse specified dimensions");
13471347
}
13481348

1349+
// Bail on non-canonical ranges.
1350+
SmallVector<Range> loopRanges =
1351+
cast<LinalgOp>(genericOp.getOperation())
1352+
.createLoopRanges(rewriter, genericOp.getLoc());
1353+
auto opFoldIsConstantValue = [](OpFoldResult ofr, int64_t value) {
1354+
if (auto attr = ofr.dyn_cast<Attribute>())
1355+
return attr.cast<IntegerAttr>().getInt() == value;
1356+
llvm::APInt actual;
1357+
return matchPattern(ofr.get<Value>(), m_ConstantInt(&actual)) &&
1358+
actual.getSExtValue() == value;
1359+
};
1360+
if (!llvm::all_of(loopRanges, [&](Range range) {
1361+
return opFoldIsConstantValue(range.offset, 0) &&
1362+
opFoldIsConstantValue(range.stride, 1);
1363+
})) {
1364+
return rewriter.notifyMatchFailure(
1365+
genericOp,
1366+
"expected all loop ranges to have zero start and unit stride");
1367+
}
1368+
13491369
// Get the iterator types for the operand.
13501370
SmallVector<StringRef> iteratorTypes = getCollapsedOpIteratorTypes(
13511371
genericOp.iterator_types().getValue(), collapsingInfo);
@@ -1390,17 +1410,10 @@ static FailureOr<SmallVector<Value>> collapseGenericOpIterationDims(
13901410
// Collect the loop range of the generic op.
13911411
OpBuilder::InsertionGuard g(rewriter);
13921412
rewriter.setInsertionPoint(collapsedGenericOp);
1393-
SmallVector<Range> loopRanges =
1394-
cast<LinalgOp>(genericOp.getOperation())
1395-
.createLoopRanges(rewriter, genericOp.getLoc());
1396-
assert(llvm::all_of(loopRanges,
1397-
[](Range range) {
1398-
return matchPattern(range.offset, m_Zero()) &&
1399-
matchPattern(range.stride, m_One());
1400-
}) &&
1401-
"expected all loop ranges to have zero start and unit stride");
1402-
SmallVector<Value> loopBound = llvm::to_vector(
1403-
llvm::map_range(loopRanges, [](Range range) { return range.size; }));
1413+
SmallVector<Value> loopBound =
1414+
llvm::to_vector(llvm::map_range(loopRanges, [&](Range range) {
1415+
return materializeOpFoldResult(rewriter, loc, range.size);
1416+
}));
14041417
generateCollapsedIndexingRegion(loc,
14051418
&collapsedGenericOp->getRegion(0).front(),
14061419
collapsingInfo, loopBound, rewriter);

mlir/lib/Dialect/Linalg/Transforms/Fusion.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,22 +117,21 @@ static LinalgOp fuse(OpBuilder &b, LinalgOp producer,
117117
SmallVector<Range, 8> loopRanges;
118118
Location loc = producer.getLoc();
119119
auto zero = b.create<arith::ConstantIndexOp>(loc, 0);
120-
auto one = b.create<arith::ConstantIndexOp>(loc, 1);
121120

122121
for (unsigned i = 0, e = producer.getNumLoops(); i < e; ++i) {
123122
auto shapeDim = getShapeDefiningLoopRange(producer, i);
124123
Value dim = createOrFoldDimOp(b, loc, shapeDim.shape, shapeDim.dimension);
125124
sizeBounds.push_back(dim);
126125
auto it = fusedLoopsAndRanges.find(i);
127126
if (it != fusedLoopsAndRanges.end()) {
128-
ivs.push_back(it->second.offset);
129-
tileSizes.push_back(it->second.size);
127+
ivs.push_back(materializeOpFoldResult(b, loc, it->second.offset));
128+
tileSizes.push_back(materializeOpFoldResult(b, loc, it->second.size));
130129
loopRanges.push_back(it->second);
131130
LLVM_DEBUG(llvm::dbgs() << "tiled loop#" << i << " with LoopRange "
132131
<< loopRanges.back() << "\n");
133132
} else {
134133
tileSizes.push_back(zero);
135-
loopRanges.push_back(Range{zero, dim, one});
134+
loopRanges.push_back(Range{b.getIndexAttr(0), dim, b.getIndexAttr(1)});
136135
LLVM_DEBUG(llvm::dbgs() << "full loop#" << i << " with LoopRange "
137136
<< loopRanges.back() << "\n");
138137
}
@@ -168,8 +167,9 @@ static LinalgOp fuse(OpBuilder &b, LinalgOp producer,
168167

169168
// Shift all IndexOp results by the tile offset.
170169
SmallVector<Value> allIvs;
171-
llvm::transform(loopRanges, std::back_inserter(allIvs),
172-
[](Range range) { return range.offset; });
170+
llvm::transform(loopRanges, std::back_inserter(allIvs), [&](Range range) {
171+
return materializeOpFoldResult(b, loc, range.offset);
172+
});
173173
offsetIndices(b, clonedOp, allIvs);
174174

175175
return clonedOp;

mlir/lib/Dialect/Linalg/Transforms/FusionOnTensors.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,9 @@ static LinalgOp getTiledProducer(OpBuilder &b, OpResult producerResult,
143143
// Obtain the `producerOp` loop bounds and the `sliceOp` ranges.
144144
SmallVector<Value> producerLoopBounds;
145145
llvm::transform(producerOp.createLoopRanges(b, loc),
146-
std::back_inserter(producerLoopBounds),
147-
[](Range range) { return range.size; });
146+
std::back_inserter(producerLoopBounds), [&](Range range) {
147+
return materializeOpFoldResult(b, loc, range.size);
148+
});
148149
SmallVector<Range> sliceOpRanges = sliceOp.getOrCreateRanges(b, loc);
149150

150151
// Tile the producer operands given the `sliceOp` ranges. Iterate the
@@ -157,8 +158,10 @@ static LinalgOp getTiledProducer(OpBuilder &b, OpResult producerResult,
157158
for (auto it : zip(tiledSliceDimIndices, tiledProducerLoopIndices)) {
158159
int64_t tiledSliceDim = std::get<0>(it);
159160
int64_t tiledProducerLoop = std::get<1>(it);
160-
tileIvs[tiledProducerLoop] = sliceOpRanges[tiledSliceDim].offset;
161-
tileSizes[tiledProducerLoop] = sliceOpRanges[tiledSliceDim].size;
161+
tileIvs[tiledProducerLoop] =
162+
materializeOpFoldResult(b, loc, sliceOpRanges[tiledSliceDim].offset);
163+
tileSizes[tiledProducerLoop] =
164+
materializeOpFoldResult(b, loc, sliceOpRanges[tiledSliceDim].size);
162165
allIvs[tiledProducerLoop] = tileIvs[tiledProducerLoop];
163166
}
164167
erase_value(tileIvs, nullptr);

mlir/lib/Dialect/Linalg/Transforms/Promotion.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,20 @@ FailureOr<PromotionInfo> mlir::linalg::promoteSubviewAsNewBuffer(
223223
if (droppedDims[en.index()])
224224
continue;
225225
auto rangeValue = en.value();
226-
// Try to extract a tight constant.
226+
// Try to extract a tight constant. If the size is known statically, no need
227+
// to look for the bound.
227228
LLVM_DEBUG(llvm::dbgs() << "Extract tightest: " << rangeValue.size << "\n");
228-
FailureOr<int64_t> upperBound =
229-
getConstantUpperBoundForIndex(rangeValue.size);
230-
Value size =
231-
failed(upperBound)
232-
? rangeValue.size
233-
: b.create<arith::ConstantIndexOp>(loc, upperBound.value());
229+
Value size;
230+
if (auto attr = rangeValue.size.dyn_cast<Attribute>()) {
231+
size = materializeOpFoldResult(b, loc, rangeValue.size);
232+
} else {
233+
Value materializedSize = materializeOpFoldResult(b, loc, rangeValue.size);
234+
FailureOr<int64_t> upperBound =
235+
getConstantUpperBoundForIndex(materializedSize);
236+
size = failed(upperBound)
237+
? materializedSize
238+
: b.create<arith::ConstantIndexOp>(loc, upperBound.getValue());
239+
}
234240
LLVM_DEBUG(llvm::dbgs() << "Extracted tightest: " << size << "\n");
235241
fullSizes.push_back(size);
236242
partialSizes.push_back(

mlir/lib/Dialect/Linalg/Transforms/Split.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,10 @@ linalg::splitOp(RewriterBase &rewriter, TilingInterface op, unsigned dimension,
7474
if (dimension >= iterationSpace.size())
7575
return std::make_pair(op, TilingInterface());
7676

77-
SmallVector<OpFoldResult> offsets =
78-
getAsOpFoldResult(llvm::to_vector(llvm::map_range(
79-
iterationSpace, [](const Range &range) { return range.offset; })));
80-
SmallVector<OpFoldResult> sizes =
81-
getAsOpFoldResult(llvm::to_vector(llvm::map_range(
82-
iterationSpace, [](const Range &range) { return range.size; })));
77+
SmallVector<OpFoldResult> offsets = llvm::to_vector(llvm::map_range(
78+
iterationSpace, [](const Range &range) { return range.offset; }));
79+
SmallVector<OpFoldResult> sizes = llvm::to_vector(llvm::map_range(
80+
iterationSpace, [](const Range &range) { return range.size; }));
8381

8482
// Adjust the split point so that it doesn't overflow the size.
8583
AffineExpr d0, d1, d2;
@@ -105,7 +103,7 @@ linalg::splitOp(RewriterBase &rewriter, TilingInterface op, unsigned dimension,
105103
TilingInterface firstPart = createSplitPart(
106104
rewriter, op.getLoc(), op, offsets, sizes,
107105
op.getDestinationOperands(rewriter), dimension, minSplitPoint,
108-
getAsOpFoldResult(iterationSpace[dimension].offset), firstResults);
106+
iterationSpace[dimension].offset, firstResults);
109107

110108
// Need to pretend that the original op now takes as operands firstResults,
111109
// otherwise tiling interface implementation will take the wrong value to

mlir/lib/Dialect/Linalg/Transforms/Tiling.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ mlir::linalg::makeTiledLoopRanges(RewriterBase &b, Location loc, AffineMap map,
6666
// Create a new range with the applied tile sizes.
6767
SmallVector<Range, 4> res;
6868
for (unsigned idx = 0, e = tileSizes.size(); idx < e; ++idx)
69-
res.push_back(Range{b.create<arith::ConstantIndexOp>(loc, 0),
70-
shapeSizes[idx], tileSizes[idx]});
69+
res.push_back(Range{b.getIndexAttr(0), shapeSizes[idx], tileSizes[idx]});
7170
return std::make_tuple(res, loopIndexToRangeIndex);
7271
}
7372

@@ -567,10 +566,12 @@ static LogicalResult tilePadOp(RewriterBase &builder, tensor::PadOp op,
567566
SmallVector<Range> ranges = tilingInterface.getIterationDomain(builder);
568567
SmallVector<Value> lbs, dims, allDims, steps;
569568
for (int64_t i = 0; i < rank; ++i) {
570-
allDims.push_back(ranges[i].size);
569+
Value materializedSize =
570+
materializeOpFoldResult(builder, loc, ranges[i].size);
571+
allDims.push_back(materializedSize);
571572
if (!isZero(tileSizes[i])) {
572-
lbs.push_back(ranges[i].offset);
573-
dims.push_back(ranges[i].size);
573+
lbs.push_back(materializeOpFoldResult(builder, loc, ranges[i].offset));
574+
dims.push_back(materializedSize);
574575
steps.push_back(tileSizes[i]);
575576
}
576577
}

mlir/lib/Dialect/Linalg/Utils/Utils.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,14 @@ template struct mlir::linalg::GenerateLoopNest<AffineForOp>;
129129

130130
/// Given a list of subview ranges, extract individual values for lower, upper
131131
/// bounds and steps and put them into the corresponding vectors.
132-
static void unpackRanges(ArrayRef<Range> ranges, SmallVectorImpl<Value> &lbs,
132+
static void unpackRanges(OpBuilder &builder, Location loc,
133+
ArrayRef<Range> ranges, SmallVectorImpl<Value> &lbs,
133134
SmallVectorImpl<Value> &ubs,
134135
SmallVectorImpl<Value> &steps) {
135136
for (Range range : ranges) {
136-
lbs.emplace_back(range.offset);
137-
ubs.emplace_back(range.size);
138-
steps.emplace_back(range.stride);
137+
lbs.emplace_back(materializeOpFoldResult(builder, loc, range.offset));
138+
ubs.emplace_back(materializeOpFoldResult(builder, loc, range.size));
139+
steps.emplace_back(materializeOpFoldResult(builder, loc, range.stride));
139140
}
140141
}
141142

@@ -524,7 +525,7 @@ void GenerateLoopNest<scf::ForOp>::doit(
524525
}
525526

526527
SmallVector<Value, 4> lbs, ubs, steps;
527-
unpackRanges(loopRanges, lbs, ubs, steps);
528+
unpackRanges(b, loc, loopRanges, lbs, ubs, steps);
528529
LoopNest loopNest = mlir::scf::buildLoopNest(
529530
b, loc, lbs, ubs, steps, iterArgInitValues,
530531
[&](OpBuilder &b, Location loc, ValueRange ivs, ValueRange iterArgs) {
@@ -567,7 +568,7 @@ void GenerateLoopNest<AffineForOp>::doit(
567568
SmallVector<Value> iterArgInitValues = linalgOp.getOutputTensorOperands();
568569
assert(iterArgInitValues.empty() && "unexpected AffineForOp init values");
569570
SmallVector<Value, 4> lbs, ubs, steps;
570-
unpackRanges(loopRanges, lbs, ubs, steps);
571+
unpackRanges(b, loc, loopRanges, lbs, ubs, steps);
571572

572573
// Affine loops require constant steps.
573574
SmallVector<int64_t, 4> constantSteps;
@@ -744,7 +745,7 @@ void GenerateLoopNest<scf::ParallelOp>::doit(
744745
stepsStorage.reserve(numLoops);
745746

746747
// Get the loop lb, ub, and step.
747-
unpackRanges(loopRanges, lbsStorage, ubsStorage, stepsStorage);
748+
unpackRanges(b, loc, loopRanges, lbsStorage, ubsStorage, stepsStorage);
748749

749750
// Modify the lb, ub, and step based on the distribution options.
750751
SmallVector<DistributionMethod, 0> distributionMethod;
@@ -986,6 +987,12 @@ Value materializeOpFoldResult(ImplicitLocOpBuilder &builder,
986987
return builder.create<arith::ConstantIndexOp>(attr.getValue().getSExtValue());
987988
}
988989

990+
Value materializeOpFoldResult(OpBuilder &builder, Location loc,
991+
OpFoldResult opFoldResult) {
992+
ImplicitLocOpBuilder b(loc, builder);
993+
return materializeOpFoldResult(b, opFoldResult);
994+
}
995+
989996
SmallVector<Value, 4> makeTiledShapes(OpBuilder &b, Location loc,
990997
LinalgOp linalgOp,
991998
ArrayRef<Value> valuesToTile,

mlir/lib/Dialect/SCF/Transforms/TileUsingInterface.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "mlir/Dialect/Affine/IR/AffineOps.h"
1616
#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
17+
#include "mlir/Dialect/Arithmetic/Utils/Utils.h"
1718
#include "mlir/Dialect/Func/IR/FuncOps.h"
1819
#include "mlir/Dialect/SCF/Utils/Utils.h"
1920
#include "mlir/Dialect/Tensor/IR/Tensor.h"
@@ -117,23 +118,25 @@ generateTileLoopNest(OpBuilder &builder, Location loc,
117118
AffineMap minMap = AffineMap::get(1, 2, {s0, s1 - d0}, builder.getContext());
118119

119120
for (auto loopRange : llvm::enumerate(loopRanges)) {
121+
Value offset =
122+
getValueOrCreateConstantIndexOp(builder, loc, loopRange.value().offset);
123+
Value size =
124+
getValueOrCreateConstantIndexOp(builder, loc, loopRange.value().size);
120125
// No loops if tile size is zero. Set offset and size to the loop
121126
// offset and size.
122127
if (matchPattern(tileSizeVals[loopRange.index()], m_Zero())) {
123-
offsets[loopRange.index()] = loopRange.value().offset;
124-
sizes[loopRange.index()] = loopRange.value().size;
128+
offsets[loopRange.index()] = offset;
129+
sizes[loopRange.index()] = size;
125130
continue;
126131
}
127132

128133
auto loop = builder.create<scf::ForOp>(
129-
loc, loopRange.value().offset, loopRange.value().size,
130-
tileSizeVals[loopRange.index()], ValueRange{},
134+
loc, offset, size, tileSizeVals[loopRange.index()], ValueRange{},
131135
[&](OpBuilder &bodyBuilder, Location bodyLoc, Value iv,
132136
ValueRange /*iterArgs*/) {
133137
Value boundedTileSize = builder.create<AffineMinOp>(
134138
bodyLoc, minMap,
135-
ValueRange{iv, tileSizeVals[loopRange.index()],
136-
loopRange.value().size});
139+
ValueRange{iv, tileSizeVals[loopRange.index()], size});
137140
sizes[loopRange.index()] = boundedTileSize;
138141
builder.create<scf::YieldOp>(loc);
139142
});

0 commit comments

Comments
 (0)