Skip to content

Commit d3445c9

Browse files
committed
[mlir][vector] Make the in_bounds attribute mandatory
Makes the `in_bounds` attribute for vector.transfer_read and vector.transfer_write Ops mandatory. In addition, makes the Asm printer always print this attribute - tests are updated accordingly. 1. Updates in tests - default `in_bounds` value Originally, most tests would skip the `in_bounds` attribute - this was equivalent to setting all values to `false` [1]. With this change, this has to be done explicitly when writing a test. Note, especially when reviewing this change, that the vast majority of newly inserted `in_bounds` attributes are set to `false` to preserve the original semantics of the tests. There is only one exception - for broadcast dimensions the newly inserted `in_bounds` attribute is set to `true`. As per [2]: ``` vector.transfer_read op requires broadcast dimensions to be in-bounds ``` This matches the original semantics: * the `in_bounds` attribute in the context of broadcast dimensions would only be checked when present, * the verifier wasn't aware of the default value set in [1], This means that effectively, the attribute was set to `false` even for broadcast dims, but the verifier wasn't aware of that. This change makes that behaviour more explicit by setting the attribute to `true` for broadcast dims. In all other cases, the attribute is set to `false` - if that's not the case, consider that as a typo. 2. Updates in tests - 0-D vectors Reading and writing to/from 0D vectors also requires the `in_bounds` attribute. In this case, the attribute has to be empty: ```mlir vector.transfer_write %5, %m1[] {in_bounds=[]} : vector<f32>, memref<f32> ``` 3. Updates in tests - CHECK lines With this PR, the `in_bounds` attribute is always print. This required updating the `CHECK` lines that previously assumed that the attribute would be skipped. To keep this type of changes simple, I've only added `{{.*}}` to make sure that tests pass. 4. Changes in "Vectorization.cpp" The following patterns are updated to explicitly set the `in_bounds` attribute to `false`: * `LinalgCopyVTRForwardingPattern` and `LinalgCopyVTWForwardingPattern` 5. Changes in "SuperVectorize.cpp" and "Vectorization.cpp" The updates in `vectorizeAffineLoad` (SuperVectorize.cpp) and `vectorizeAsLinalgGeneric` (Vectorization.cpp) are introduced to make sure that xfer Ops created by these vectorisers set the dimension corresponding to broadcast dims as "in bounds". Otherwise, the Op verifier would fail. Note that there is no mechanism to verify whether the corresponding memory access are indeed in bounds. Previously, when `in_bounds` was optional, the verification would skip checking the attribute if it wasn't present. However, it would default to `false` in other places. Put differently, this change does not change the existing behaviour, it merely makes it more explicit. [1] https://github.com/llvm/llvm-project/blob/4145ad2bac4bb99d5034d60c74bb2789f6c6e802/mlir/include/mlir/Interfaces/VectorInterfaces.td#L243-L246 [2] https://mlir.llvm.org/docs/Dialects/Vector/#vectortransfer_read-vectortransferreadop
1 parent d7da0ae commit d3445c9

File tree

9 files changed

+81
-43
lines changed

9 files changed

+81
-43
lines changed

mlir/include/mlir/Dialect/Vector/IR/VectorOps.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,7 +1363,7 @@ def Vector_TransferReadOp :
13631363
AffineMapAttr:$permutation_map,
13641364
AnyType:$padding,
13651365
Optional<VectorOf<[I1]>>:$mask,
1366-
OptionalAttr<BoolArrayAttr>:$in_bounds)>,
1366+
BoolArrayAttr:$in_bounds)>,
13671367
Results<(outs AnyVectorOfAnyRank:$vector)> {
13681368

13691369
let summary = "Reads a supervector from memory into an SSA vector value.";
@@ -1607,7 +1607,7 @@ def Vector_TransferWriteOp :
16071607
Variadic<Index>:$indices,
16081608
AffineMapAttr:$permutation_map,
16091609
Optional<VectorOf<[I1]>>:$mask,
1610-
OptionalAttr<BoolArrayAttr>:$in_bounds)>,
1610+
BoolArrayAttr:$in_bounds)>,
16111611
Results<(outs Optional<AnyRankedTensor>:$result)> {
16121612

16131613
let summary = "The vector.transfer_write op writes a supervector to memory.";

mlir/include/mlir/IR/AffineMap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ class AffineMap {
156156
bool isMinorIdentityWithBroadcasting(
157157
SmallVectorImpl<unsigned> *broadcastedDims = nullptr) const;
158158

159+
// TODO: Document
160+
void
161+
getBroadcastDims(SmallVectorImpl<unsigned> *broadcastedDims = nullptr) const;
162+
159163
/// Return true if this affine map can be converted to a minor identity with
160164
/// broadcast by doing a permute. Return a permutation (there may be
161165
/// several) to apply to get to a minor identity with broadcasts.

mlir/include/mlir/Interfaces/VectorInterfaces.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def VectorTransferOpInterface : OpInterface<"VectorTransferOpInterface"> {
9898
dimension whether it is in-bounds or not. (Broadcast dimensions are
9999
always in-bounds).
100100
}],
101-
/*retTy=*/"::std::optional<::mlir::ArrayAttr>",
101+
/*retTy=*/"::mlir::ArrayAttr",
102102
/*methodName=*/"getInBounds",
103103
/*args=*/(ins)
104104
>,
@@ -242,7 +242,7 @@ def VectorTransferOpInterface : OpInterface<"VectorTransferOpInterface"> {
242242
return true;
243243
if (!$_op.getInBounds())
244244
return false;
245-
auto inBounds = ::llvm::cast<::mlir::ArrayAttr>(*$_op.getInBounds());
245+
auto inBounds = $_op.getInBounds();
246246
return ::llvm::cast<::mlir::BoolAttr>(inBounds[dim]).getValue();
247247
}
248248

mlir/lib/Dialect/Affine/Transforms/SuperVectorize.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1223,8 +1223,20 @@ static Operation *vectorizeAffineLoad(AffineLoadOp loadOp,
12231223
LLVM_DEBUG(dbgs() << "\n[early-vect]+++++ permutationMap: ");
12241224
LLVM_DEBUG(permutationMap.print(dbgs()));
12251225

1226+
// Make sure that the in_bounds attribute corresponding to a broadcast dim
1227+
// is set to `true` - that's required by the xfer Op.
1228+
// FIXME: We're not veryfying whether the corresponding access is in bounds.
1229+
// TODO: Use masking instead.
1230+
SmallVector<unsigned> broadcastedDims = {};
1231+
permutationMap.getBroadcastDims(&broadcastedDims);
1232+
SmallVector<bool> inBounds(vectorType.getRank(), false);
1233+
1234+
for (auto idx : broadcastedDims)
1235+
inBounds[idx] = true;
1236+
12261237
auto transfer = state.builder.create<vector::TransferReadOp>(
1227-
loadOp.getLoc(), vectorType, loadOp.getMemRef(), indices, permutationMap);
1238+
loadOp.getLoc(), vectorType, loadOp.getMemRef(), indices, permutationMap,
1239+
ArrayRef<bool>(inBounds));
12281240

12291241
// Register replacement for future uses in the scope.
12301242
state.registerOpVectorReplacement(loadOp, transfer);

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

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,8 +1338,18 @@ vectorizeAsLinalgGeneric(RewriterBase &rewriter, VectorizationState &state,
13381338

13391339
SmallVector<Value> indices(linalgOp.getShape(opOperand).size(), zero);
13401340

1341+
// Make sure that the in_bounds attribute corresponding to a broadcast dim
1342+
// is `true`
1343+
SmallVector<unsigned> broadcastedDims = {};
1344+
readMap.getBroadcastDims(&broadcastedDims);
1345+
SmallVector<bool> inBounds(readType.getRank(), false);
1346+
1347+
for (auto idx : broadcastedDims)
1348+
inBounds[idx] = true;
1349+
13411350
Operation *read = rewriter.create<vector::TransferReadOp>(
1342-
loc, readType, opOperand->get(), indices, readMap);
1351+
loc, readType, opOperand->get(), indices, readMap,
1352+
ArrayRef<bool>(inBounds));
13431353
read = state.maskOperation(rewriter, read, linalgOp, maskingMap);
13441354
Value readValue = read->getResult(0);
13451355

@@ -2676,11 +2686,12 @@ LogicalResult LinalgCopyVTRForwardingPattern::matchAndRewrite(
26762686
// The `masked` attribute is only valid on this padded buffer.
26772687
// When forwarding to vector.transfer_read, the attribute must be reset
26782688
// conservatively.
2689+
auto vectorType = xferOp.getVectorType();
26792690
Value res = rewriter.create<vector::TransferReadOp>(
2680-
xferOp.getLoc(), xferOp.getVectorType(), in, xferOp.getIndices(),
2691+
xferOp.getLoc(), vectorType, in, xferOp.getIndices(),
26812692
xferOp.getPermutationMapAttr(), xferOp.getPadding(), xferOp.getMask(),
2682-
// in_bounds is explicitly reset
2683-
/*inBoundsAttr=*/ArrayAttr());
2693+
rewriter.getBoolArrayAttr(
2694+
SmallVector<bool>(vectorType.getRank(), false)));
26842695

26852696
if (maybeFillOp)
26862697
rewriter.eraseOp(maybeFillOp);
@@ -2734,11 +2745,12 @@ LogicalResult LinalgCopyVTWForwardingPattern::matchAndRewrite(
27342745
// The `masked` attribute is only valid on this padded buffer.
27352746
// When forwarding to vector.transfer_write, the attribute must be reset
27362747
// conservatively.
2748+
auto vector = xferOp.getVector();
27372749
rewriter.create<vector::TransferWriteOp>(
2738-
xferOp.getLoc(), xferOp.getVector(), out, xferOp.getIndices(),
2750+
xferOp.getLoc(), vector, out, xferOp.getIndices(),
27392751
xferOp.getPermutationMapAttr(), xferOp.getMask(),
2740-
// in_bounds is explicitly reset
2741-
/*inBoundsAttr=*/ArrayAttr());
2752+
rewriter.getBoolArrayAttr(
2753+
SmallVector<bool>(vector.getType().getRank(), false)));
27422754

27432755
rewriter.eraseOp(copyOp);
27442756
rewriter.eraseOp(xferOp);

mlir/lib/Dialect/Vector/IR/VectorOps.cpp

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3817,7 +3817,8 @@ void TransferReadOp::build(OpBuilder &builder, OperationState &result,
38173817
auto permutationMapAttr = AffineMapAttr::get(permutationMap);
38183818
auto inBoundsAttr = (inBounds && !inBounds.value().empty())
38193819
? builder.getBoolArrayAttr(inBounds.value())
3820-
: ArrayAttr();
3820+
: builder.getBoolArrayAttr(
3821+
SmallVector<bool>(vectorType.getRank(), false));
38213822
build(builder, result, vectorType, source, indices, permutationMapAttr,
38223823
inBoundsAttr);
38233824
}
@@ -3832,7 +3833,8 @@ void TransferReadOp::build(OpBuilder &builder, OperationState &result,
38323833
auto permutationMapAttr = AffineMapAttr::get(permutationMap);
38333834
auto inBoundsAttr = (inBounds && !inBounds.value().empty())
38343835
? builder.getBoolArrayAttr(inBounds.value())
3835-
: ArrayAttr();
3836+
: builder.getBoolArrayAttr(
3837+
SmallVector<bool>(vectorType.getRank(), false));
38363838
build(builder, result, vectorType, source, indices, permutationMapAttr,
38373839
padding,
38383840
/*mask=*/Value(), inBoundsAttr);
@@ -3950,17 +3952,15 @@ verifyTransferOp(VectorTransferOpInterface op, ShapedType shapedType,
39503952
<< inferredMaskType << ") and mask operand type (" << maskType
39513953
<< ") don't match";
39523954

3953-
if (inBounds) {
3954-
if (permutationMap.getNumResults() != static_cast<int64_t>(inBounds.size()))
3955-
return op->emitOpError("expects the optional in_bounds attr of same rank "
3956-
"as permutation_map results: ")
3957-
<< AffineMapAttr::get(permutationMap)
3958-
<< " vs inBounds of size: " << inBounds.size();
3959-
for (unsigned int i = 0; i < permutationMap.getNumResults(); ++i)
3960-
if (isa<AffineConstantExpr>(permutationMap.getResult(i)) &&
3961-
!llvm::cast<BoolAttr>(inBounds.getValue()[i]).getValue())
3962-
return op->emitOpError("requires broadcast dimensions to be in-bounds");
3963-
}
3955+
if (permutationMap.getNumResults() != static_cast<int64_t>(inBounds.size()))
3956+
return op->emitOpError("expects the in_bounds attr of same rank "
3957+
"as permutation_map results: ")
3958+
<< AffineMapAttr::get(permutationMap)
3959+
<< " vs inBounds of size: " << inBounds.size();
3960+
for (unsigned int i = 0; i < permutationMap.getNumResults(); ++i)
3961+
if (isa<AffineConstantExpr>(permutationMap.getResult(i)) &&
3962+
!llvm::cast<BoolAttr>(inBounds.getValue()[i]).getValue())
3963+
return op->emitOpError("requires broadcast dimensions to be in-bounds");
39643964

39653965
return success();
39663966
}
@@ -3970,9 +3970,6 @@ static void printTransferAttrs(OpAsmPrinter &p, VectorTransferOpInterface op) {
39703970
elidedAttrs.push_back(TransferReadOp::getOperandSegmentSizeAttr());
39713971
if (op.getPermutationMap().isMinorIdentity())
39723972
elidedAttrs.push_back(op.getPermutationMapAttrName());
3973-
// Elide in_bounds attribute if all dims are out-of-bounds.
3974-
if (llvm::none_of(op.getInBoundsValues(), [](bool b) { return b; }))
3975-
elidedAttrs.push_back(op.getInBoundsAttrName());
39763973
p.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
39773974
}
39783975

@@ -4080,8 +4077,7 @@ LogicalResult TransferReadOp::verify() {
40804077

40814078
if (failed(verifyTransferOp(cast<VectorTransferOpInterface>(getOperation()),
40824079
shapedType, vectorType, maskType,
4083-
inferredMaskType, permutationMap,
4084-
getInBounds() ? *getInBounds() : ArrayAttr())))
4080+
inferredMaskType, permutationMap, getInBounds())))
40854081
return failure();
40864082

40874083
if (auto sourceVectorElementType =
@@ -4354,9 +4350,11 @@ void TransferWriteOp::build(OpBuilder &builder, OperationState &result,
43544350
AffineMap permutationMap,
43554351
std::optional<ArrayRef<bool>> inBounds) {
43564352
auto permutationMapAttr = AffineMapAttr::get(permutationMap);
4357-
auto inBoundsAttr = (inBounds && !inBounds.value().empty())
4358-
? builder.getBoolArrayAttr(inBounds.value())
4359-
: ArrayAttr();
4353+
auto inBoundsAttr =
4354+
(inBounds && !inBounds.value().empty())
4355+
? builder.getBoolArrayAttr(inBounds.value())
4356+
: builder.getBoolArrayAttr(SmallVector<bool>(
4357+
llvm::cast<VectorType>(vector.getType()).getRank(), false));
43604358
build(builder, result, vector, dest, indices, permutationMapAttr,
43614359
/*mask=*/Value(), inBoundsAttr);
43624360
}
@@ -4462,8 +4460,7 @@ LogicalResult TransferWriteOp::verify() {
44624460

44634461
if (failed(verifyTransferOp(cast<VectorTransferOpInterface>(getOperation()),
44644462
shapedType, vectorType, maskType,
4465-
inferredMaskType, permutationMap,
4466-
getInBounds() ? *getInBounds() : ArrayAttr())))
4463+
inferredMaskType, permutationMap, getInBounds())))
44674464
return failure();
44684465

44694466
return verifyPermutationMap(permutationMap,

mlir/lib/Dialect/Vector/Transforms/LowerVectorMask.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ struct MaskedTransferReadOpPattern
224224
rewriter.replaceOpWithNewOp<TransferReadOp>(
225225
maskingOp.getOperation(), readOp.getVectorType(), readOp.getSource(),
226226
readOp.getIndices(), readOp.getPermutationMap(), readOp.getPadding(),
227-
maskingOp.getMask(), readOp.getInBounds().value_or(ArrayAttr()));
227+
maskingOp.getMask(), readOp.getInBounds());
228228
return success();
229229
}
230230
};
@@ -246,7 +246,7 @@ struct MaskedTransferWriteOpPattern
246246
rewriter.replaceOpWithNewOp<TransferWriteOp>(
247247
maskingOp.getOperation(), resultType, writeOp.getVector(),
248248
writeOp.getSource(), writeOp.getIndices(), writeOp.getPermutationMap(),
249-
maskingOp.getMask(), writeOp.getInBounds().value_or(ArrayAttr()));
249+
maskingOp.getMask(), writeOp.getInBounds());
250250
return success();
251251
}
252252
};

mlir/lib/Dialect/Vector/Transforms/LowerVectorTransfer.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,7 @@ struct TransferReadPermutationLowering
133133

134134
// Transpose in_bounds attribute.
135135
ArrayAttr newInBoundsAttr =
136-
op.getInBounds() ? inverseTransposeInBoundsAttr(
137-
rewriter, op.getInBounds().value(), permutation)
138-
: ArrayAttr();
136+
inverseTransposeInBoundsAttr(rewriter, op.getInBounds(), permutation);
139137

140138
// Generate new transfer_read operation.
141139
VectorType newReadType = VectorType::get(
@@ -208,9 +206,7 @@ struct TransferWritePermutationLowering
208206

209207
// Transpose in_bounds attribute.
210208
ArrayAttr newInBoundsAttr =
211-
op.getInBounds() ? inverseTransposeInBoundsAttr(
212-
rewriter, op.getInBounds().value(), permutation)
213-
: ArrayAttr();
209+
inverseTransposeInBoundsAttr(rewriter, op.getInBounds(), permutation);
214210

215211
// Generate new transfer_write operation.
216212
Value newVec = rewriter.create<vector::TransposeOp>(

mlir/lib/IR/AffineMap.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,23 @@ bool AffineMap::isMinorIdentityWithBroadcasting(
188188
return true;
189189
}
190190

191+
void AffineMap::getBroadcastDims(
192+
SmallVectorImpl<unsigned> *broadcastedDims) const {
193+
if (broadcastedDims)
194+
broadcastedDims->clear();
195+
for (const auto &idxAndExpr : llvm::enumerate(getResults())) {
196+
unsigned resIdx = idxAndExpr.index();
197+
AffineExpr expr = idxAndExpr.value();
198+
if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
199+
// Each result may be either a constant 0 (broadcasted dimension).
200+
if (constExpr.getValue() != 0)
201+
continue;
202+
if (broadcastedDims)
203+
broadcastedDims->push_back(resIdx);
204+
}
205+
}
206+
}
207+
191208
/// Return true if this affine map can be converted to a minor identity with
192209
/// broadcast by doing a permute. Return a permutation (there may be
193210
/// several) to apply to get to a minor identity with broadcasts.

0 commit comments

Comments
 (0)