Skip to content

Commit f34fadf

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 9f4a25e commit f34fadf

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
@@ -155,6 +155,10 @@ class AffineMap {
155155
bool isMinorIdentityWithBroadcasting(
156156
SmallVectorImpl<unsigned> *broadcastedDims = nullptr) const;
157157

158+
// TODO: Document
159+
void
160+
getBroadcastDims(SmallVectorImpl<unsigned> *broadcastedDims = nullptr) const;
161+
158162
/// Return true if this affine map can be converted to a minor identity with
159163
/// broadcast by doing a permute. Return a permutation (there may be
160164
/// 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
@@ -1343,8 +1343,18 @@ vectorizeAsLinalgGeneric(RewriterBase &rewriter, VectorizationState &state,
13431343

13441344
SmallVector<Value> indices(linalgOp.getShape(opOperand).size(), zero);
13451345

1346+
// Make sure that the in_bounds attribute corresponding to a broadcast dim
1347+
// is `true`
1348+
SmallVector<unsigned> broadcastedDims = {};
1349+
readMap.getBroadcastDims(&broadcastedDims);
1350+
SmallVector<bool> inBounds(readType.getRank(), false);
1351+
1352+
for (auto idx : broadcastedDims)
1353+
inBounds[idx] = true;
1354+
13461355
Operation *read = rewriter.create<vector::TransferReadOp>(
1347-
loc, readType, opOperand->get(), indices, readMap);
1356+
loc, readType, opOperand->get(), indices, readMap,
1357+
ArrayRef<bool>(inBounds));
13481358
read = state.maskOperation(rewriter, read, linalgOp, maskingMap);
13491359
Value readValue = read->getResult(0);
13501360

@@ -2681,11 +2691,12 @@ LogicalResult LinalgCopyVTRForwardingPattern::matchAndRewrite(
26812691
// The `masked` attribute is only valid on this padded buffer.
26822692
// When forwarding to vector.transfer_read, the attribute must be reset
26832693
// conservatively.
2694+
auto vectorType = xferOp.getVectorType();
26842695
Value res = rewriter.create<vector::TransferReadOp>(
2685-
xferOp.getLoc(), xferOp.getVectorType(), in, xferOp.getIndices(),
2696+
xferOp.getLoc(), vectorType, in, xferOp.getIndices(),
26862697
xferOp.getPermutationMapAttr(), xferOp.getPadding(), xferOp.getMask(),
2687-
// in_bounds is explicitly reset
2688-
/*inBoundsAttr=*/ArrayAttr());
2698+
rewriter.getBoolArrayAttr(
2699+
SmallVector<bool>(vectorType.getRank(), false)));
26892700

26902701
if (maybeFillOp)
26912702
rewriter.eraseOp(maybeFillOp);
@@ -2739,11 +2750,12 @@ LogicalResult LinalgCopyVTWForwardingPattern::matchAndRewrite(
27392750
// The `masked` attribute is only valid on this padded buffer.
27402751
// When forwarding to vector.transfer_write, the attribute must be reset
27412752
// conservatively.
2753+
auto vector = xferOp.getVector();
27422754
rewriter.create<vector::TransferWriteOp>(
2743-
xferOp.getLoc(), xferOp.getVector(), out, xferOp.getIndices(),
2755+
xferOp.getLoc(), vector, out, xferOp.getIndices(),
27442756
xferOp.getPermutationMapAttr(), xferOp.getMask(),
2745-
// in_bounds is explicitly reset
2746-
/*inBoundsAttr=*/ArrayAttr());
2757+
rewriter.getBoolArrayAttr(
2758+
SmallVector<bool>(vector.getType().getRank(), false)));
27472759

27482760
rewriter.eraseOp(copyOp);
27492761
rewriter.eraseOp(xferOp);

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

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

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

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

@@ -4081,8 +4078,7 @@ LogicalResult TransferReadOp::verify() {
40814078

40824079
if (failed(verifyTransferOp(cast<VectorTransferOpInterface>(getOperation()),
40834080
shapedType, vectorType, maskType,
4084-
inferredMaskType, permutationMap,
4085-
getInBounds() ? *getInBounds() : ArrayAttr())))
4081+
inferredMaskType, permutationMap, getInBounds())))
40864082
return failure();
40874083

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

44644462
if (failed(verifyTransferOp(cast<VectorTransferOpInterface>(getOperation()),
44654463
shapedType, vectorType, maskType,
4466-
inferredMaskType, permutationMap,
4467-
getInBounds() ? *getInBounds() : ArrayAttr())))
4464+
inferredMaskType, permutationMap, getInBounds())))
44684465
return failure();
44694466

44704467
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
@@ -187,6 +187,23 @@ bool AffineMap::isMinorIdentityWithBroadcasting(
187187
return true;
188188
}
189189

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

0 commit comments

Comments
 (0)