[mlir] Improve TransferOp verifier: broadcasts are in_bounds

Broadcast dimensions of vector transfer ops are always in-bounds. This is consistent with the fact that the starting position of a transfer is always in-bounds.

Differential Revision: https://reviews.llvm.org/D102566
This commit is contained in:
Matthias Springer 2021-05-17 22:26:48 +09:00
parent 9b7e5b63aa
commit 2c9688d201
4 changed files with 46 additions and 12 deletions

View File

@ -1245,11 +1245,17 @@ def Vector_TransferReadOp :
`padding`. Elements whose corresponding mask element is `0` are masked out.
An optional boolean array attribute is provided to specify which dimensions
of the transfer are guaranteed to be within bounds. The absence of this
`in_bounds` attribute signifies that any dimension of the transfer may be
out-of-bounds. A `vector.transfer_read` can be lowered to a simple load if
all dimensions are specified to be within bounds and no `mask` was
specified.
of the transfer are guaranteed to be within bounds. The length of the array
must equal the rank of the vector type. Broadcast dimensions must always be
in-bounds. The absence of this optional `in_bounds` attribute signifies that
any dimension of the transfer (except for broadcasts) may be out-of-bounds.
A `vector.transfer_read` can be lowered to a simple load if all dimensions
are specified to be within bounds and no `mask` was specified.
Note that `in_bounds` is specified for result dimensions and not input
dimensions. The starting point of the transfer, i.e.,
`%A[%expr1, %expr2, %expr3, %expr4]` in the example below, is expected to
be in-bounds and as indices are increasing, accesses may run out-of-bounds.
This operation is called 'read' by opposition to 'load' because the
super-vector granularity is generally not representable with a single
@ -1423,7 +1429,8 @@ def Vector_TransferWriteOp :
[affine-map](Affine.md#affine-maps) which specifies the transposition on the
slice to match the vector shape. The permutation map may be implicit and
omitted from parsing and printing if it is the canonical minor identity map
(i.e. if it does not permute or broadcast any dimension).
(i.e. if it does not permute any dimension). In contrast to `transfer_read`,
write ops cannot have broadcast dimensions.
The size of the slice is specified by the size of the vector.
@ -1438,6 +1445,19 @@ def Vector_TransferWriteOp :
if all dimensions are specified to be within bounds and no `mask` was
specified.
An optional boolean array attribute is provided to specify which dimensions
of the transfer are guaranteed to be within bounds. The length of the array
must equal the rank of the vector type. The absence of this optional
`in_bounds` attribute signifies that any dimension of the transfer
may be out-of-bounds. A `vector.transfer_write` can be lowered to a simple
store if all dimensions are specified to be within bounds and no `mask` was
specified.
Note that `in_bounds` is specified for result dimensions and not input
dimensions. The starting point of the transfer, i.e.,
`%A[%expr1, %expr2, %expr3, %expr4]` in the example below, is expected to
be in-bounds and as indices are increasing, accesses may run out-of-bounds.
This operation is called 'write' by opposition to 'store' because the
super-vector granularity is generally not representable with a single
hardware register. A `vector.transfer_write` is thus a

View File

@ -69,17 +69,17 @@ def VectorTransferOpInterface : OpInterface<"VectorTransferOpInterface"> {
/*defaultImplementation=*/ [{ return "permutation_map"; }]
>,
InterfaceMethod<
/*desc=*/[{
Return `true` when the `in_bounds` attribute at dimension
`dim` is set to `true`. Return `false` otherwise.}],
/*desc=*/[{ Return `true` if dimension `dim` is in-bounds. Return `false`
otherwise. }],
/*retTy=*/"bool",
/*methodName=*/"isDimInBounds",
/*args=*/(ins "unsigned":$dim),
/*methodBody=*/"",
/*defaultImplementation=*/[{
return $_op.in_bounds() &&
$_op.in_bounds()->template cast<ArrayAttr>()[dim]
.template cast<BoolAttr>().getValue();
return $_op.isBroadcastDim(dim)
|| ($_op.in_bounds()
&& $_op.in_bounds()->template cast<ArrayAttr>()[dim]
.template cast<BoolAttr>().getValue());
}]
>,
InterfaceMethod<

View File

@ -2386,6 +2386,10 @@ static LogicalResult verifyTransferOp(Operation *op, ShapedType shapedType,
return op->emitOpError("expects the optional in_bounds attr of same rank "
"as permutation_map results: ")
<< AffineMapAttr::get(permutationMap);
for (unsigned int i = 0; i < permutationMap.getNumResults(); ++i)
if (permutationMap.getResult(i).isa<AffineConstantExpr>()
&& !inBounds.getValue()[i].cast<BoolAttr>().getValue())
return op->emitOpError("requires broadcast dimensions to be in-bounds");
}
return success();

View File

@ -367,6 +367,16 @@ func @test_vector.transfer_read(%arg0: memref<?x?xvector<2x3xf32>>) {
// -----
func @test_vector.transfer_read(%arg0: memref<?x?xvector<2x3xf32>>) {
%c3 = constant 3 : index
%f0 = constant 0.0 : f32
%vf0 = splat %f0 : vector<2x3xf32>
// expected-error@+1 {{requires broadcast dimensions to be in-bounds}}
%0 = vector.transfer_read %arg0[%c3, %c3], %vf0 {in_bounds = [false, true], permutation_map = affine_map<(d0, d1)->(0, d1)>} : memref<?x?xvector<2x3xf32>>, vector<1x1x2x3xf32>
}
// -----
func @test_vector.transfer_read(%arg0: memref<?x?xvector<2x3xf32>>) {
%c3 = constant 3 : index
%f0 = constant 0.0 : f32