diff --git a/mlir/include/mlir/Dialect/Vector/VectorOps.td b/mlir/include/mlir/Dialect/Vector/VectorOps.td --- a/mlir/include/mlir/Dialect/Vector/VectorOps.td +++ b/mlir/include/mlir/Dialect/Vector/VectorOps.td @@ -1245,11 +1245,17 @@ `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 @@ [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 @@ 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 diff --git a/mlir/include/mlir/Interfaces/VectorInterfaces.td b/mlir/include/mlir/Interfaces/VectorInterfaces.td --- a/mlir/include/mlir/Interfaces/VectorInterfaces.td +++ b/mlir/include/mlir/Interfaces/VectorInterfaces.td @@ -69,17 +69,17 @@ /*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()[dim] - .template cast().getValue(); + return $_op.isBroadcastDim(dim) + || ($_op.in_bounds() + && $_op.in_bounds()->template cast()[dim] + .template cast().getValue()); }] >, InterfaceMethod< diff --git a/mlir/lib/Dialect/Vector/VectorOps.cpp b/mlir/lib/Dialect/Vector/VectorOps.cpp --- a/mlir/lib/Dialect/Vector/VectorOps.cpp +++ b/mlir/lib/Dialect/Vector/VectorOps.cpp @@ -2386,6 +2386,10 @@ 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() + && !inBounds.getValue()[i].cast().getValue()) + return op->emitOpError("requires broadcast dimensions to be in-bounds"); } return success(); diff --git a/mlir/test/Dialect/Vector/invalid.mlir b/mlir/test/Dialect/Vector/invalid.mlir --- a/mlir/test/Dialect/Vector/invalid.mlir +++ b/mlir/test/Dialect/Vector/invalid.mlir @@ -367,6 +367,16 @@ // ----- +func @test_vector.transfer_read(%arg0: memref>) { + %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>, vector<1x1x2x3xf32> +} + +// ----- + func @test_vector.transfer_read(%arg0: memref>) { %c3 = constant 3 : index %f0 = constant 0.0 : f32