forked from OSchip/llvm-project
[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:
parent
9b7e5b63aa
commit
2c9688d201
|
@ -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
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue