[Mlir-commits] [mlir] [mlir][vector] Fix crash on untraceable masks in getCompressedMaskOp (PR #207299)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Fri Jul 3 10:14:38 PDT 2026
https://github.com/marquisburg updated https://github.com/llvm/llvm-project/pull/207299
>From bb179a3054918920bf0f77f7d8b141d176e630de Mon Sep 17 00:00:00 2001
From: GhostDragonWasTook <marquis at marquiseco.co.za>
Date: Fri, 3 Jul 2026 02:40:25 +0200
Subject: [PATCH 1/2] [mlir][vector] Fix crash on untraceable masks in
getCompressedMaskOp
The loop tracing a mask back to its creation op assumed the chain
always ends at a create_mask/constant_mask/arith.constant, a
block-argument mask made it fall through to isa<> on a null op
(segfault), and any other unhandled defining op spun it forever.
Fixes #206928
---
.../Transforms/VectorEmulateNarrowType.cpp | 8 +++++-
.../emulate-narrow-type-unsupported.mlir | 28 +++++++++++++++++++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp b/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp
index 9faaebdcf8f35..e37296e50fb29 100644
--- a/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp
+++ b/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp
@@ -98,10 +98,16 @@ static FailureOr<Operation *> getCompressedMaskOp(OpBuilder &rewriter,
if (auto extractOp = dyn_cast<vector::ExtractOp>(maskOp)) {
maskOp = extractOp.getSource().getDefiningOp();
extractOps.push_back(extractOp);
+ } else {
+ // Unsupported mask-defining op (e.g. a block argument, which has no
+ // defining op, or an op we cannot trace through). Bail out rather than
+ // looping forever or dereferencing a null op below.
+ break;
}
}
- if (!isa<arith::ConstantOp, vector::CreateMaskOp, vector::ConstantMaskOp>(
+ if (!maskOp ||
+ !isa<arith::ConstantOp, vector::CreateMaskOp, vector::ConstantMaskOp>(
maskOp))
return failure();
diff --git a/mlir/test/Dialect/Vector/emulate-narrow-type-unsupported.mlir b/mlir/test/Dialect/Vector/emulate-narrow-type-unsupported.mlir
index a5a6fc4acfe10..74f72ed5a5d9c 100644
--- a/mlir/test/Dialect/Vector/emulate-narrow-type-unsupported.mlir
+++ b/mlir/test/Dialect/Vector/emulate-narrow-type-unsupported.mlir
@@ -109,3 +109,31 @@ func.func @vector_maskedstore_2d_i8_negative(%arg0: index, %arg1: index, %arg2:
// CHECK-LABEL: func @vector_maskedstore_2d_i8_negative
// CHECK: memref.alloc() : memref<3x8xi8>
// CHECK-NOT: i32
+
+// -----
+
+// The mask is a block argument, so its defining op cannot be traced back to a
+// supported mask-creation op. This must gracefully bail out instead of crashing
+// (see llvm/llvm-project#206928).
+
+func.func @vector_maskedstore_i8_block_arg_mask_negative(%arg0: memref<?xi8>, %mask: vector<8xi1>, %value: vector<8xi8>) {
+ %c0 = arith.constant 0 : index
+ vector.maskedstore %arg0[%c0], %mask, %value : memref<?xi8>, vector<8xi1>, vector<8xi8>
+ return
+}
+
+// CHECK-LABEL: func @vector_maskedstore_i8_block_arg_mask_negative
+// CHECK: vector.maskedstore
+
+// -----
+
+// As above, but for `vector.maskedload`.
+
+func.func @vector_maskedload_i8_block_arg_mask_negative(%arg0: memref<?xi8>, %mask: vector<8xi1>, %passthru: vector<8xi8>) -> vector<8xi8> {
+ %c0 = arith.constant 0 : index
+ %0 = vector.maskedload %arg0[%c0], %mask, %passthru : memref<?xi8>, vector<8xi1>, vector<8xi8> into vector<8xi8>
+ return %0 : vector<8xi8>
+}
+
+// CHECK-LABEL: func @vector_maskedload_i8_block_arg_mask_negative
+// CHECK: vector.maskedload
>From 8ce3d9fdeae633ea27cca1ab59c1625d3ca0e465 Mon Sep 17 00:00:00 2001
From: GhostDragonWasTook <marquis at marquiseco.co.za>
Date: Fri, 3 Jul 2026 18:52:14 +0200
Subject: [PATCH 2/2] [mlir][vector] Fix unaligned-offset miscompiles in
narrow-type emulation
ConvertVectorLoad, ConvertVectorMaskedLoad, and ConvertVectorTransferRead
treated a vector size divisble by the container size as proof that the access
starts on a container boundary, and ConvertVectorMaskedStore never looked
at the offset at all. Any size-divisible access at an unaligned offset
(e.g. vector<8xi4> at index 1) was silently read or written one sub-element early.
Always fold the real intra-container offset: loads reuse the existing static/dynamic
extraction paths, and maskedstore bails out unless the offset provably folds to zero,
the same contract I saw in #189235 for vector.store.
---
.../Transforms/VectorEmulateNarrowType.cpp | 54 +--
...atten-memref-and-emulate-narrow-types.mlir | 11 +-
...e-narrow-type-unaligned-dynamic-store.mlir | 49 +++
.../vector-emulate-narrow-type-unaligned.mlir | 70 ++++
.../Vector/vector-emulate-narrow-type.mlir | 312 ++++++++++++------
5 files changed, 369 insertions(+), 127 deletions(-)
diff --git a/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp b/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp
index e37296e50fb29..54c79ebce7289 100644
--- a/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp
+++ b/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp
@@ -910,6 +910,18 @@ struct ConvertVectorMaskedStore final
Value linearizedIndices =
getValueOrCreateConstantIndexOp(rewriter, loc, linearizedIndicesOfr);
+ // The load-select-store sequence below assumes the store starts at a
+ // container element boundary. A non-zero intra-container offset would
+ // require shifting both the mask and the value to store; that is not
+ // implemented yet, so bail out to avoid storing at the wrong offset.
+ // Note: a size divisible by `emulatedPerContainerElem` does NOT imply
+ // such alignment - the offset depends on the (possibly dynamic) indices.
+ std::optional<int64_t> intraDataOffset =
+ getConstantIntValue(linearizedInfo.intraDataOffset);
+ if (!intraDataOffset || *intraDataOffset != 0)
+ return rewriter.notifyMatchFailure(
+ op, "unaligned or dynamically-offset maskedstore is not supported");
+
// Load the whole data and use arith.select to handle the corner cases.
//
// As an example, for this masked store of i4 values:
@@ -1042,8 +1054,6 @@ struct ConvertVectorLoad final : OpConversionPattern<vector::LoadOp> {
// compile time as they must be constants.
auto origElements = op.getVectorType().getNumElements();
- // Note, per-element-alignment was already verified above.
- bool isDivisibleInSize = origElements % emulatedPerContainerElem == 0;
auto stridedMetadata =
memref::ExtractStridedMetadataOp::create(rewriter, loc, op.getBase());
@@ -1058,9 +1068,11 @@ struct ConvertVectorLoad final : OpConversionPattern<vector::LoadOp> {
stridedMetadata.getConstifiedMixedStrides(),
getAsOpFoldResult(adaptor.getIndices()));
+ // Note: a size divisible by `emulatedPerContainerElem` does NOT imply the
+ // load starts at a container element boundary - the intra-container
+ // offset depends on the (possibly dynamic) indices.
std::optional<int64_t> foldedIntraVectorOffset =
- isDivisibleInSize ? 0
- : getConstantIntValue(linearizedInfo.intraDataOffset);
+ getConstantIntValue(linearizedInfo.intraDataOffset);
// Always load enough elements which can cover the original elements.
int64_t maxintraDataOffset =
@@ -1077,7 +1089,9 @@ struct ConvertVectorLoad final : OpConversionPattern<vector::LoadOp> {
result = dynamicallyExtractSubVector(
rewriter, loc, dyn_cast<TypedValue<VectorType>>(result), resultVector,
linearizedInfo.intraDataOffset, origElements);
- } else if (!isDivisibleInSize) {
+ } else {
+ // This is a no-op if the offset is zero and all loaded elements are
+ // used.
result = staticallyExtractSubvector(
rewriter, loc, result, *foldedIntraVectorOffset, origElements);
}
@@ -1167,8 +1181,6 @@ struct ConvertVectorMaskedLoad final
// subvector at the proper offset after bit-casting.
auto origType = op.getVectorType();
auto origElements = origType.getNumElements();
- // Note, per-element-alignment was already verified above.
- bool isDivisibleInSize = origElements % emulatedPerContainerElem == 0;
auto stridedMetadata =
memref::ExtractStridedMetadataOp::create(rewriter, loc, op.getBase());
@@ -1182,9 +1194,11 @@ struct ConvertVectorMaskedLoad final
stridedMetadata.getConstifiedMixedStrides(),
getAsOpFoldResult(adaptor.getIndices()));
+ // Note: a size divisible by `emulatedPerContainerElem` does NOT imply the
+ // load starts at a container element boundary - the intra-container
+ // offset depends on the (possibly dynamic) indices.
std::optional<int64_t> foldedIntraVectorOffset =
- isDivisibleInSize ? 0
- : getConstantIntValue(linearizedInfo.intraDataOffset);
+ getConstantIntValue(linearizedInfo.intraDataOffset);
int64_t maxIntraDataOffset =
foldedIntraVectorOffset.value_or(emulatedPerContainerElem - 1);
@@ -1208,7 +1222,8 @@ struct ConvertVectorMaskedLoad final
passthru = dynamicallyInsertSubVector(
rewriter, loc, passthru, emptyVector, linearizedInfo.intraDataOffset,
origElements);
- } else if (!isDivisibleInSize) {
+ } else {
+ // No-op when the offset is zero and the sizes match.
passthru = staticallyInsertSubvector(rewriter, loc, passthru, emptyVector,
*foldedIntraVectorOffset);
}
@@ -1237,7 +1252,8 @@ struct ConvertVectorMaskedLoad final
mask = dynamicallyInsertSubVector(rewriter, loc, mask, emptyMask,
linearizedInfo.intraDataOffset,
origElements);
- } else if (!isDivisibleInSize) {
+ } else {
+ // No-op when the offset is zero and the sizes match.
mask = staticallyInsertSubvector(rewriter, loc, op.getMask(), emptyMask,
*foldedIntraVectorOffset);
}
@@ -1248,7 +1264,8 @@ struct ConvertVectorMaskedLoad final
result = dynamicallyExtractSubVector(
rewriter, loc, result, op.getPassThru(),
linearizedInfo.intraDataOffset, origElements);
- } else if (!isDivisibleInSize) {
+ } else {
+ // No-op when the offset is zero and all loaded elements are used.
result = staticallyExtractSubvector(
rewriter, loc, result, *foldedIntraVectorOffset, origElements);
}
@@ -1325,10 +1342,6 @@ struct ConvertVectorTransferRead final
auto origElements = op.getVectorType().getNumElements();
- // Note, per-element-alignment was already verified above.
- bool isDivisibleInSize =
- fitsInMultiByteContainerTy(op.getVectorType(), containerElemTy);
-
// Pad the padding value with 0s on the left. These bits are discarded and
// thus their values don't matter.
Value padding = adaptor.getPadding();
@@ -1355,9 +1368,11 @@ struct ConvertVectorTransferRead final
stridedMetadata.getConstifiedMixedStrides(),
getAsOpFoldResult(adaptor.getIndices()));
+ // Note: a size divisible by `emulatedPerContainerElem` does NOT imply the
+ // read starts at a container element boundary - the intra-container
+ // offset depends on the (possibly dynamic) indices.
std::optional<int64_t> foldedIntraVectorOffset =
- isDivisibleInSize ? 0
- : getConstantIntValue(linearizedInfo.intraDataOffset);
+ getConstantIntValue(linearizedInfo.intraDataOffset);
int64_t maxIntraDataOffset =
foldedIntraVectorOffset.value_or(emulatedPerContainerElem - 1);
@@ -1382,7 +1397,8 @@ struct ConvertVectorTransferRead final
result = dynamicallyExtractSubVector(rewriter, loc, bitCast, zeros,
linearizedInfo.intraDataOffset,
origElements);
- } else if (!isDivisibleInSize) {
+ } else {
+ // No-op when the offset is zero and all read elements are used.
result = staticallyExtractSubvector(
rewriter, loc, result, *foldedIntraVectorOffset, origElements);
}
diff --git a/mlir/test/Dialect/Vector/flatten-memref-and-emulate-narrow-types.mlir b/mlir/test/Dialect/Vector/flatten-memref-and-emulate-narrow-types.mlir
index ef253624fe0b4..38f4d8b0a6ff0 100644
--- a/mlir/test/Dialect/Vector/flatten-memref-and-emulate-narrow-types.mlir
+++ b/mlir/test/Dialect/Vector/flatten-memref-and-emulate-narrow-types.mlir
@@ -31,10 +31,14 @@ func.func @vector_maskedload_2d_i4(%arg0: index, %passthru: vector<8xi4>) -> vec
// -----
+// Note: the column index must be a constant so that the intra-byte offset of
+// the store provably folds to 0 (a maskedstore at an offset that cannot be
+// proven to be byte-aligned cannot be emulated and would fail to legalize).
func.func @vector_maskedstore_2d_i4(%arg0: index, %value: vector<8xi4>) {
%0 = memref.alloc() : memref<4x8xi4>
+ %c0 = arith.constant 0 : index
%mask = vector.constant_mask [5] : vector<8xi1>
- vector.maskedstore %0[%arg0, %arg0], %mask, %value :
+ vector.maskedstore %0[%arg0, %c0], %mask, %value :
memref<4x8xi4>, vector<8xi1>, vector<8xi4>
return
}
@@ -54,9 +58,12 @@ func.func @vector_store_2d_i4(%arg0: index, %value: vector<8xi4>) {
// -----
+// Note: the column index is a constant so that the intra-byte offset of the
+// read provably folds to 0 and no extra byte needs to be read.
func.func @vector_transfer_read_2d_i4(%arg0: index, %padding: i4) -> vector<8xi4> {
%0 = memref.alloc() : memref<4x8xi4>
- %1 = vector.transfer_read %0[%arg0, %arg0], %padding {in_bounds = [true]} : memref<4x8xi4>, vector<8xi4>
+ %c0 = arith.constant 0 : index
+ %1 = vector.transfer_read %0[%arg0, %c0], %padding {in_bounds = [true]} : memref<4x8xi4>, vector<8xi4>
return %1 : vector<8xi4>
}
// CHECK-LABEL: func @vector_transfer_read_2d_i4(
diff --git a/mlir/test/Dialect/Vector/vector-emulate-narrow-type-unaligned-dynamic-store.mlir b/mlir/test/Dialect/Vector/vector-emulate-narrow-type-unaligned-dynamic-store.mlir
index 518d825234502..ba2293c6321e2 100644
--- a/mlir/test/Dialect/Vector/vector-emulate-narrow-type-unaligned-dynamic-store.mlir
+++ b/mlir/test/Dialect/Vector/vector-emulate-narrow-type-unaligned-dynamic-store.mlir
@@ -20,3 +20,52 @@ func.func @vector_store_i4_dynamic_memref(%arg0: vector<8xi4>, %dim0: index,
vector.store %arg0, %src[%idx0, %idx1] : memref<?x?xi4>, vector<8xi4>
return
}
+
+// -----
+
+// Dynamic sub-byte vector.maskedstore offsets cannot be treated as
+// byte-aligned either: the store could start in the middle of a byte and the
+// emulation (which read-modify-writes whole bytes) would write to the wrong
+// bits.
+
+func.func @vector_maskedstore_i4_dynamic_col(%idx1: index, %idx2: index,
+ %num_elements_to_store: index,
+ %value: vector<8xi4>) {
+ %0 = memref.alloc() : memref<3x8xi4>
+ %mask = vector.create_mask %num_elements_to_store : vector<8xi1>
+ // expected-error @below {{failed to legalize operation 'vector.maskedstore' that was explicitly marked illegal}}
+ vector.maskedstore %0[%idx1, %idx2], %mask, %value :
+ memref<3x8xi4>, vector<8xi1>, vector<8xi4>
+ return
+}
+
+// -----
+
+func.func @vector_maskedstore_i4_constant_mask_dynamic_col(%idx1: index,
+ %idx2: index,
+ %value: vector<8xi4>) {
+ %0 = memref.alloc() : memref<3x8xi4>
+ %mask = vector.constant_mask [4] : vector<8xi1>
+ // expected-error @below {{failed to legalize operation 'vector.maskedstore' that was explicitly marked illegal}}
+ vector.maskedstore %0[%idx1, %idx2], %mask, %value :
+ memref<3x8xi4>, vector<8xi1>, vector<8xi4>
+ return
+}
+
+// -----
+
+// A maskedstore at a static but non-container-aligned offset (an odd i4 index
+// gives intra-byte offset 1) must also be rejected - emulating it with the
+// compressed mask would silently store at the wrong bits (the intra-byte
+// offset used to be dropped; see the discussion in llvm/llvm-project#206928).
+
+func.func @vector_maskedstore_i4_unaligned_static_offset(%num_elements_to_store: index,
+ %value: vector<8xi4>) {
+ %c1 = arith.constant 1 : index
+ %0 = memref.alloc() : memref<9xi4>
+ %mask = vector.create_mask %num_elements_to_store : vector<8xi1>
+ // expected-error @below {{failed to legalize operation 'vector.maskedstore' that was explicitly marked illegal}}
+ vector.maskedstore %0[%c1], %mask, %value :
+ memref<9xi4>, vector<8xi1>, vector<8xi4>
+ return
+}
diff --git a/mlir/test/Dialect/Vector/vector-emulate-narrow-type-unaligned.mlir b/mlir/test/Dialect/Vector/vector-emulate-narrow-type-unaligned.mlir
index bec7736b90973..60ceecf88ddaf 100644
--- a/mlir/test/Dialect/Vector/vector-emulate-narrow-type-unaligned.mlir
+++ b/mlir/test/Dialect/Vector/vector-emulate-narrow-type-unaligned.mlir
@@ -586,3 +586,73 @@ func.func @vector_store_i2_2d_const_nonzero_col(%arg0: vector<4xi2>) {
// and one for byte 1 (element at position [0]).
// CHECK: memref.generic_atomic_rmw %[[ALLOC]][%[[C0]]]
// CHECK: memref.generic_atomic_rmw %[[ALLOC]][{{.+}}]
+
+// -----
+
+///----------------------------------------------------------------------------------------
+/// Size-divisible vectors at non-container-aligned static offsets.
+///
+/// A vector length divisible by the number of emulated elements per container
+/// element does NOT imply the access starts at a container element boundary.
+/// These used to be emulated as if aligned, silently reading shifted data
+/// (see the discussion in llvm/llvm-project#206928).
+///----------------------------------------------------------------------------------------
+
+func.func @vector_load_i4_unaligned_static_offset() -> vector<8xi4> {
+ %c1 = arith.constant 1 : index
+ %0 = memref.alloc() : memref<9xi4>
+ %1 = vector.load %0[%c1] : memref<9xi4>, vector<8xi4>
+ return %1 : vector<8xi4>
+}
+// CHECK: func @vector_load_i4_unaligned_static_offset(
+// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<5xi8>
+// CHECK: %[[C0:.+]] = arith.constant 0 : index
+// CHECK: %[[LOAD:.+]] = vector.load %[[ALLOC]][%[[C0]]] : memref<5xi8>, vector<5xi8>
+// CHECK: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<5xi8> to vector<10xi4>
+// CHECK: %[[SLICE:.+]] = vector.extract_strided_slice %[[BITCAST]] {offsets = [1], sizes = [8], strides = [1]} : vector<10xi4> to vector<8xi4>
+// CHECK: return %[[SLICE]]
+
+// -----
+
+func.func @vector_maskedload_i4_unaligned_static_offset(%num_elems: index, %passthru: vector<8xi4>) -> vector<8xi4> {
+ %c1 = arith.constant 1 : index
+ %0 = memref.alloc() : memref<9xi4>
+ %mask = vector.create_mask %num_elems : vector<8xi1>
+ %1 = vector.maskedload %0[%c1], %mask, %passthru : memref<9xi4>, vector<8xi1>, vector<8xi4> into vector<8xi4>
+ return %1 : vector<8xi4>
+}
+// The compressed mask accounts for the front offset of 1: ceil((n + 1) / 2).
+// CHECK: #[[MASK_MAP:.+]] = affine_map<()[s0] -> ((s0 + 1) ceildiv 2)>
+// CHECK: func @vector_maskedload_i4_unaligned_static_offset(
+// CHECK-SAME: %[[NUM_ELEMS:[a-zA-Z0-9]+]]: index, %[[PASSTHRU:[a-zA-Z0-9]+]]: vector<8xi4>)
+// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<5xi8>
+// CHECK: %[[ORIG_MASK:.+]] = vector.create_mask %[[NUM_ELEMS]] : vector<8xi1>
+// CHECK: %[[MASK_IDX:.+]] = affine.apply #[[MASK_MAP]]()[%[[NUM_ELEMS]]]
+// CHECK: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<5xi1>
+// CHECK: %[[NEW_PASSTHRU:.+]] = vector.insert_strided_slice %[[PASSTHRU]], %{{.+}} {offsets = [1], strides = [1]} : vector<8xi4> into vector<10xi4>
+// CHECK: %[[PASSTHRU_BITCAST:.+]] = vector.bitcast %[[NEW_PASSTHRU]] : vector<10xi4> to vector<5xi8>
+// CHECK: %[[C0:.+]] = arith.constant 0 : index
+// CHECK: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[C0]]], %[[NEW_MASK]], %[[PASSTHRU_BITCAST]] : memref<5xi8>, vector<5xi1>, vector<5xi8> into vector<5xi8>
+// CHECK: %[[LOAD_BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<5xi8> to vector<10xi4>
+// CHECK: %[[SHIFTED_MASK:.+]] = vector.insert_strided_slice %[[ORIG_MASK]], %{{.+}} {offsets = [1], strides = [1]} : vector<8xi1> into vector<10xi1>
+// CHECK: %[[SELECT:.+]] = arith.select %[[SHIFTED_MASK]], %[[LOAD_BITCAST]], %[[NEW_PASSTHRU]] : vector<10xi1>, vector<10xi4>
+// CHECK: %[[SLICE:.+]] = vector.extract_strided_slice %[[SELECT]] {offsets = [1], sizes = [8], strides = [1]} : vector<10xi4> to vector<8xi4>
+// CHECK: return %[[SLICE]]
+
+// -----
+
+func.func @vector_transfer_read_i4_unaligned_static_offset(%padding: i4) -> vector<8xi4> {
+ %c1 = arith.constant 1 : index
+ %0 = memref.alloc() : memref<9xi4>
+ %1 = vector.transfer_read %0[%c1], %padding {in_bounds = [true]} : memref<9xi4>, vector<8xi4>
+ return %1 : vector<8xi4>
+}
+// CHECK: func @vector_transfer_read_i4_unaligned_static_offset(
+// CHECK-SAME: %[[PADDING:[a-zA-Z0-9]+]]: i4)
+// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<5xi8>
+// CHECK: %[[PAD_I8:.+]] = arith.extui %[[PADDING]] : i4 to i8
+// CHECK: %[[C0:.+]] = arith.constant 0 : index
+// CHECK: %[[READ:.+]] = vector.transfer_read %[[ALLOC]][%[[C0]]], %[[PAD_I8]] : memref<5xi8>, vector<5xi8>
+// CHECK: %[[BITCAST:.+]] = vector.bitcast %[[READ]] : vector<5xi8> to vector<10xi4>
+// CHECK: %[[SLICE:.+]] = vector.extract_strided_slice %[[BITCAST]] {offsets = [1], sizes = [8], strides = [1]} : vector<10xi4> to vector<8xi4>
+// CHECK: return %[[SLICE]]
diff --git a/mlir/test/Dialect/Vector/vector-emulate-narrow-type.mlir b/mlir/test/Dialect/Vector/vector-emulate-narrow-type.mlir
index 72b355e0fed65..af31ac62f859e 100644
--- a/mlir/test/Dialect/Vector/vector-emulate-narrow-type.mlir
+++ b/mlir/test/Dialect/Vector/vector-emulate-narrow-type.mlir
@@ -17,14 +17,20 @@ func.func @vector_load_i8(%arg1: index, %arg2: index) -> vector<4xi8> {
// CHECK-NEXT: [[L:%.+]] = vector.load %[[ALLOC]][%[[ARG0]], %[[ARG1]]] : memref<3x4xi8>, vector<4xi8>
// CHECK-NEXT: return
-// CHECK32: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 4)>
+// The intra-container offset of the load is dynamic (it depends on %arg2), so
+// one extra container element is loaded and the target elements are extracted
+// dynamically.
+// CHECK32-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 4)>
+// CHECK32-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 4)>
// CHECK32: func @vector_load_i8(
// CHECK32-SAME: %[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK32: %[[VECLOAD:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<3xi32>, vector<1xi32>
-// CHECK32: %[[VEC_I4:.+]] = vector.bitcast %[[VECLOAD]] : vector<1xi32> to vector<4xi8>
-// CHECK32: return %[[VEC_I4]]
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK32: %[[VECLOAD:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<3xi32>, vector<2xi32>
+// CHECK32: %[[VEC_I8:.+]] = vector.bitcast %[[VECLOAD]] : vector<2xi32> to vector<8xi8>
+// CHECK32: %[[EXTRACT:.+]] = vector.extract %[[VEC_I8]][%[[FRONT]]] : i8 from vector<8xi8>
+// CHECK32-COUNT-4: vector.insert {{.+}} : i8 into vector<4xi8>
// -----
@@ -35,21 +41,32 @@ func.func @vector_load_i4(%arg1: index, %arg2: index) -> vector<3x8xi4> {
%2 = vector.insert %1, %cst [0] : vector<8xi4> into vector<3x8xi4>
return %2 : vector<3x8xi4>
}
+// The intra-container offset of the load is dynamic (it depends on %arg2), so
+// one extra container element is loaded and the target elements are extracted
+// dynamically.
// CHECK-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 * 4 + s1 floordiv 2)>
+// CHECK-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 2)>
// CHECK: func @vector_load_i4
// CHECK-SAME: (%[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<12xi8>
// CHECK: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<12xi8>, vector<4xi8>
-// CHECK: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<4xi8> to vector<8xi4>
+// CHECK: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<12xi8>, vector<5xi8>
+// CHECK: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<5xi8> to vector<10xi4>
+// CHECK: %[[EXTRACT:.+]] = vector.extract %[[VEC_I4]][%[[FRONT]]] : i4 from vector<10xi4>
+// CHECK-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// CHECK32-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 8)>
+// CHECK32-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 8)>
// CHECK32: func @vector_load_i4
// CHECK32-SAME: (%[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK32: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<3xi32>, vector<1xi32>
-// CHECK32: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<1xi32> to vector<8xi4>
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK32: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<3xi32>, vector<2xi32>
+// CHECK32: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<2xi32> to vector<16xi4>
+// CHECK32: %[[EXTRACT:.+]] = vector.extract %[[VEC_I4]][%[[FRONT]]] : i4 from vector<16xi4>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// -----
@@ -60,21 +77,32 @@ func.func @vector_load_f4(%arg1: index, %arg2: index) -> vector<3x8xf4E2M1FN> {
%2 = vector.insert %1, %cst [0] : vector<8xf4E2M1FN> into vector<3x8xf4E2M1FN>
return %2 : vector<3x8xf4E2M1FN>
}
+// The intra-container offset of the load is dynamic (it depends on %arg2), so
+// one extra container element is loaded and the target elements are extracted
+// dynamically.
// CHECK-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 * 4 + s1 floordiv 2)>
+// CHECK-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 2)>
// CHECK: func @vector_load_f4
// CHECK-SAME: (%[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<12xi8>
// CHECK: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<12xi8>, vector<4xi8>
-// CHECK: %[[VEC_F4:.+]] = vector.bitcast %[[VEC]] : vector<4xi8> to vector<8xf4E2M1FN>
+// CHECK: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<12xi8>, vector<5xi8>
+// CHECK: %[[VEC_F4:.+]] = vector.bitcast %[[VEC]] : vector<5xi8> to vector<10xf4E2M1FN>
+// CHECK: %[[EXTRACT:.+]] = vector.extract %[[VEC_F4]][%[[FRONT]]] : f4E2M1FN from vector<10xf4E2M1FN>
+// CHECK-COUNT-8: vector.insert {{.+}} : f4E2M1FN into vector<8xf4E2M1FN>
// CHECK32-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 8)>
+// CHECK32-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 8)>
// CHECK32: func @vector_load_f4
// CHECK32-SAME: (%[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK32: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<3xi32>, vector<1xi32>
-// CHECK32: %[[VEC_F4:.+]] = vector.bitcast %[[VEC]] : vector<1xi32> to vector<8xf4E2M1FN>
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK32: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<3xi32>, vector<2xi32>
+// CHECK32: %[[VEC_F4:.+]] = vector.bitcast %[[VEC]] : vector<2xi32> to vector<16xf4E2M1FN>
+// CHECK32: %[[EXTRACT:.+]] = vector.extract %[[VEC_F4]][%[[FRONT]]] : f4E2M1FN from vector<16xf4E2M1FN>
+// CHECK32-COUNT-8: vector.insert {{.+}} : f4E2M1FN into vector<8xf4E2M1FN>
// -----
@@ -83,8 +111,11 @@ func.func @vector_load_i4_dynamic(%arg0 : index, %arg1 : index, %arg2 : index, %
%1 = vector.load %0[%arg2, %arg3] : memref<?x?xi4>, vector<8xi4>
return %1 : vector<8xi4>
}
+// The intra-container offset of the load is dynamic, so one extra container
+// element is loaded and the target elements are extracted dynamically.
// CHECK-DAG: #[[MAP0:.+]] = affine_map<()[s0, s1] -> ((s0 * s1) floordiv 2, s0 floordiv 2)>
// CHECK-DAG: #[[MAP1:.+]] = affine_map<()[s0, s1, s2] -> ((s2 + s0 * s1) floordiv 2)>
+// CHECK-DAG: #[[MAP2:.+]] = affine_map<()[s0, s1, s2] -> (s0 * s1 + s2 - ((s2 + s0 * s1) floordiv 2) * 2)>
// CHECK: func.func @vector_load_i4_dynamic(
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9_]+]]: index
// CHECK-SAME: %[[ARG1:[a-zA-Z0-9_]+]]: index
@@ -93,11 +124,15 @@ func.func @vector_load_i4_dynamic(%arg0 : index, %arg1 : index, %arg2 : index, %
// CHECK: %[[SIZE:.+]] = affine.max #[[MAP0]]()[%[[ARG1]], %[[ARG0]]]
// CHECK: %[[ALLOC:.+]] = memref.alloc(%[[SIZE]]) : memref<?xi8>
// CHECK: %[[INDEX:.+]] = affine.apply #[[MAP1]]()[%[[ARG2]], %[[ARG1]], %[[ARG3]]]
-// CHECK: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<?xi8>, vector<4xi8>
-// CHECK: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<4xi8> to vector<8xi4>
+// CHECK: %[[FRONT:.+]] = affine.apply #[[MAP2]]()[%[[ARG2]], %[[ARG1]], %[[ARG3]]]
+// CHECK: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<?xi8>, vector<5xi8>
+// CHECK: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<5xi8> to vector<10xi4>
+// CHECK: %[[EXTRACT:.+]] = vector.extract %[[VEC_I4]][%[[FRONT]]] : i4 from vector<10xi4>
+// CHECK-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// CHECK32-DAG: #[[MAP0:.+]] = affine_map<()[s0, s1] -> ((s0 * s1) floordiv 8, s0 floordiv 8)>
// CHECK32-DAG: #[[MAP1:.+]] = affine_map<()[s0, s1, s2] -> ((s2 + s0 * s1) floordiv 8)>
+// CHECK32-DAG: #[[MAP2:.+]] = affine_map<()[s0, s1, s2] -> (s0 * s1 + s2 - ((s2 + s0 * s1) floordiv 8) * 8)>
// CHECK32: func.func @vector_load_i4_dynamic(
// CHECK32-SAME: %[[ARG0:[a-zA-Z0-9_]+]]: index
// CHECK32-SAME: %[[ARG1:[a-zA-Z0-9_]+]]: index
@@ -106,8 +141,11 @@ func.func @vector_load_i4_dynamic(%arg0 : index, %arg1 : index, %arg2 : index, %
// CHECK32: %[[SIZE:.+]] = affine.max #[[MAP0]]()[%[[ARG1]], %[[ARG0]]]
// CHECK32: %[[ALLOC:.+]] = memref.alloc(%[[SIZE]]) : memref<?xi32>
// CHECK32: %[[INDEX:.+]] = affine.apply #[[MAP1]]()[%[[ARG2]], %[[ARG1]], %[[ARG3]]]
-// CHECK32: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<?xi32>, vector<1xi32>
-// CHECK32: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<1xi32> to vector<8xi4>
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[MAP2]]()[%[[ARG2]], %[[ARG1]], %[[ARG3]]]
+// CHECK32: %[[VEC:.+]] = vector.load %[[ALLOC]][%[[INDEX]]] : memref<?xi32>, vector<2xi32>
+// CHECK32: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<2xi32> to vector<16xi4>
+// CHECK32: %[[EXTRACT:.+]] = vector.extract %[[VEC_I4]][%[[FRONT]]] : i4 from vector<16xi4>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// -----
@@ -122,25 +160,36 @@ func.func @vector_transfer_read_i4(%arg1: index, %arg2: index) -> vector<8xi4> {
memref<3x8xi4>, vector<8xi4>
return %1 : vector<8xi4>
}
+// The intra-container offset of the read is dynamic (it depends on %arg2), so
+// one extra container element is read and the target elements are extracted
+// dynamically.
// CHECK-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 * 4 + s1 floordiv 2)>
+// CHECK-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 2)>
// CHECK: func @vector_transfer_read_i4
// CHECK-SAME: (%[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK: %[[CONST:.+]] = arith.constant 0 : i4
// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<12xi8>
// CHECK: %[[PAD:.+]] = arith.extui %[[CONST]] : i4 to i8
// CHECK: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK: %[[VEC:.+]] = vector.transfer_read %[[ALLOC]][%[[INDEX]]], %[[PAD]] : memref<12xi8>, vector<4xi8>
-// CHECK: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<4xi8> to vector<8xi4>
+// CHECK: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK: %[[VEC:.+]] = vector.transfer_read %[[ALLOC]][%[[INDEX]]], %[[PAD]] : memref<12xi8>, vector<5xi8>
+// CHECK: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<5xi8> to vector<10xi4>
+// CHECK: %[[EXTRACT:.+]] = vector.extract %[[VEC_I4]][%[[FRONT]]] : i4 from vector<10xi4>
+// CHECK-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// CHECK32-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 8)>
+// CHECK32-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 8)>
// CHECK32: func @vector_transfer_read_i4
// CHECK32-SAME: (%[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK32: %[[CONST:.+]] = arith.constant 0 : i4
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[PAD:.+]] = arith.extui %[[CONST]] : i4 to i32
// CHECK32: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK32: %[[VEC:.+]] = vector.transfer_read %[[ALLOC]][%[[INDEX]]], %[[PAD]] : memref<3xi32>, vector<1xi32>
-// CHECK32: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<1xi32> to vector<8xi4>
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK32: %[[VEC:.+]] = vector.transfer_read %[[ALLOC]][%[[INDEX]]], %[[PAD]] : memref<3xi32>, vector<2xi32>
+// CHECK32: %[[VEC_I4:.+]] = vector.bitcast %[[VEC]] : vector<2xi32> to vector<16xi4>
+// CHECK32: %[[EXTRACT:.+]] = vector.extract %[[VEC_I4]][%[[FRONT]]] : i4 from vector<16xi4>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// -----
@@ -151,7 +200,11 @@ func.func @vector_transfer_read_f4(%arg1: index, %arg2: index) -> vector<8xf4E2M
memref<3x8xf4E2M1FN>, vector<8xf4E2M1FN>
return %1 : vector<8xf4E2M1FN>
}
+// The intra-container offset of the read is dynamic (it depends on %arg2), so
+// one extra container element is read and the target elements are extracted
+// dynamically.
// CHECK-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 * 4 + s1 floordiv 2)>
+// CHECK-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 2)>
// CHECK: func @vector_transfer_read_f4
// CHECK-SAME: (%[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK: %[[CONST:.+]] = arith.constant 0.{{0+}}e+00 : f4E2M1FN
@@ -159,10 +212,14 @@ func.func @vector_transfer_read_f4(%arg1: index, %arg2: index) -> vector<8xf4E2M
// CHECK: %[[BC:.+]] = arith.bitcast %[[CONST]] : f4E2M1FN to i4
// CHECK: %[[PAD:.+]] = arith.extui %[[BC]] : i4 to i8
// CHECK: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK: %[[VEC:.+]] = vector.transfer_read %[[ALLOC]][%[[INDEX]]], %[[PAD]] : memref<12xi8>, vector<4xi8>
-// CHECK: %[[VEC_F4:.+]] = vector.bitcast %[[VEC]] : vector<4xi8> to vector<8xf4E2M1FN>
+// CHECK: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK: %[[VEC:.+]] = vector.transfer_read %[[ALLOC]][%[[INDEX]]], %[[PAD]] : memref<12xi8>, vector<5xi8>
+// CHECK: %[[VEC_F4:.+]] = vector.bitcast %[[VEC]] : vector<5xi8> to vector<10xf4E2M1FN>
+// CHECK: %[[EXTRACT:.+]] = vector.extract %[[VEC_F4]][%[[FRONT]]] : f4E2M1FN from vector<10xf4E2M1FN>
+// CHECK-COUNT-8: vector.insert {{.+}} : f4E2M1FN into vector<8xf4E2M1FN>
// CHECK32-DAG: #[[MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 8)>
+// CHECK32-DAG: #[[MAP1:.+]] = affine_map<()[s0] -> (s0 mod 8)>
// CHECK32: func @vector_transfer_read_f4
// CHECK32-SAME: (%[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index)
// CHECK32: %[[CONST:.+]] = arith.constant 0.{{0+}}e+00 : f4E2M1FN
@@ -170,8 +227,11 @@ func.func @vector_transfer_read_f4(%arg1: index, %arg2: index) -> vector<8xf4E2M
// CHECK32: %[[BC:.+]] = arith.bitcast %[[CONST]] : f4E2M1FN to i4
// CHECK32: %[[PAD:.+]] = arith.extui %[[BC]] : i4 to i32
// CHECK32: %[[INDEX:.+]] = affine.apply #[[MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK32: %[[VEC:.+]] = vector.transfer_read %[[ALLOC]][%[[INDEX]]], %[[PAD]] : memref<3xi32>, vector<1xi32>
-// CHECK32: %[[VEC_F4:.+]] = vector.bitcast %[[VEC]] : vector<1xi32> to vector<8xf4E2M1FN>
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[MAP1]]()[%[[ARG1]]]
+// CHECK32: %[[VEC:.+]] = vector.transfer_read %[[ALLOC]][%[[INDEX]]], %[[PAD]] : memref<3xi32>, vector<2xi32>
+// CHECK32: %[[VEC_F4:.+]] = vector.bitcast %[[VEC]] : vector<2xi32> to vector<16xf4E2M1FN>
+// CHECK32: %[[EXTRACT:.+]] = vector.extract %[[VEC_F4]][%[[FRONT]]] : f4E2M1FN from vector<16xf4E2M1FN>
+// CHECK32-COUNT-8: vector.insert {{.+}} : f4E2M1FN into vector<8xf4E2M1FN>
// -----
@@ -196,22 +256,29 @@ func.func @vector_maskedload_i8(%arg1: index, %arg2: index, %arg3: index, %passt
// CHECK-SAME: memref<3x4xi8>, vector<4xi1>, vector<4xi8> into vector<4xi8>
// CHECK-NEXT: return
+// The intra-container offset of the load is dynamic (it depends on %arg2), so
+// one extra container element is loaded; the mask, the passthru and the result
+// are shifted dynamically by the front offset.
// CHECK32-DAG: #[[LOAD_IDX_MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 4)>
-// CHECK32-DAG: #[[MASK_IDX_MAP:.+]] = affine_map<()[s0] -> (s0 ceildiv 4)>
+// CHECK32-DAG: #[[FRONT_MAP:.+]] = affine_map<()[s0] -> (s0 mod 4)>
+// CHECK32-DAG: #[[MASK_IDX_MAP:.+]] = affine_map<()[s0] -> ((s0 + 3) ceildiv 4)>
// CHECK32: func @vector_maskedload_i8(
// CHECK32-SAME: %[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index,
// CHECK32-SAME: %[[ARG2:[a-zA-Z0-9]+]]: index, %[[ARG3:[a-zA-Z0-9]+]]: vector<4xi8>)
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[ORIG_MASK:.+]] = vector.create_mask %[[ARG2]] : vector<4xi1>
// CHECK32: %[[LD_IDX:.+]] = affine.apply #[[LOAD_IDX_MAP]]()[%[[ARG0]], %[[ARG1]]]
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[FRONT_MAP]]()[%[[ARG1]]]
// CHECK32: %[[MASK_IDX:.+]] = affine.apply #[[MASK_IDX_MAP]]()[%[[ARG2]]]
-// CHECK32: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<1xi1>
-// CHECK32: %[[NEW_PASSTHRU:.+]] = vector.bitcast %[[ARG3]] : vector<4xi8> to vector<1xi32>
+// CHECK32: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<2xi1>
+// CHECK32-COUNT-4: vector.insert {{.+}} : i8 into vector<8xi8>
+// CHECK32: %[[NEW_PASSTHRU:.+]] = vector.bitcast %{{.+}} : vector<8xi8> to vector<2xi32>
// CHECK32: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LD_IDX]]], %[[NEW_MASK]], %[[NEW_PASSTHRU]] :
-// CHECK32-SAME: memref<3xi32>, vector<1xi1>, vector<1xi32> into vector<1xi32>
-// CHECK32: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<1xi32> to vector<4xi8>
-// CHECK32: %[[SELECT:.+]] = arith.select %[[ORIG_MASK]], %[[BITCAST]], %[[ARG3]] : vector<4xi1>, vector<4xi8>
-// CHECK32: return %[[SELECT]]
+// CHECK32-SAME: memref<3xi32>, vector<2xi1>, vector<2xi32> into vector<2xi32>
+// CHECK32: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<2xi32> to vector<8xi8>
+// CHECK32-COUNT-4: vector.insert {{.+}} : i1 into vector<8xi1>
+// CHECK32: %[[SELECT:.+]] = arith.select %{{.+}}, %[[BITCAST]], %{{.+}} : vector<8xi1>, vector<8xi8>
+// CHECK32-COUNT-4: vector.insert {{.+}} : i8 into vector<4xi8>
// -----
@@ -224,37 +291,50 @@ func.func @vector_maskedload_i4(%arg1: index, %arg2: index, %arg3: index, %passt
%2 = vector.insert %1, %cst [0] : vector<8xi4> into vector<3x8xi4>
return %2 : vector<3x8xi4>
}
+// The intra-container offset of the load is dynamic (it depends on %arg2), so
+// one extra container element is loaded; the mask, the passthru and the result
+// are shifted dynamically by the front offset.
// CHECK-DAG: #[[LOAD_IDX_MAP:.+]] = affine_map<()[s0, s1] -> (s0 * 4 + s1 floordiv 2)>
-// CHECK-DAG: #[[MASK_IDX_MAP:.+]] = affine_map<()[s0] -> (s0 ceildiv 2)>
+// CHECK-DAG: #[[FRONT_MAP:.+]] = affine_map<()[s0] -> (s0 mod 2)>
+// CHECK-DAG: #[[MASK_IDX_MAP:.+]] = affine_map<()[s0] -> ((s0 + 1) ceildiv 2)>
// CHECK: func @vector_maskedload_i4(
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index,
// CHECK-SAME: %[[ARG2:[a-zA-Z0-9]+]]: index, %[[ARG3:[a-zA-Z0-9]+]]: vector<8xi4>)
// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<12xi8>
// CHECK: %[[ORIG_MASK:.+]] = vector.create_mask %[[ARG2]] : vector<8xi1>
// CHECK: %[[LD_IDX:.+]] = affine.apply #[[LOAD_IDX_MAP]]()[%[[ARG0]], %[[ARG1]]]
+// CHECK: %[[FRONT:.+]] = affine.apply #[[FRONT_MAP]]()[%[[ARG1]]]
// CHECK: %[[MASK_IDX:.+]] = affine.apply #[[MASK_IDX_MAP]]()[%[[ARG2]]]
-// CHECK: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<4xi1>
-// CHECK: %[[NEW_PASSTHRU:.+]] = vector.bitcast %[[ARG3]] : vector<8xi4> to vector<4xi8>
+// CHECK: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<5xi1>
+// CHECK-COUNT-8: vector.insert {{.+}} : i4 into vector<10xi4>
+// CHECK: %[[NEW_PASSTHRU:.+]] = vector.bitcast %{{.+}} : vector<10xi4> to vector<5xi8>
// CHECK: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LD_IDX]]], %[[NEW_MASK]], %[[NEW_PASSTHRU]] :
-// CHECK-SAME: memref<12xi8>, vector<4xi1>, vector<4xi8> into vector<4xi8>
-// CHECK: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<4xi8> to vector<8xi4>
-// CHECK: %[[SELECT:.+]] = arith.select %[[ORIG_MASK]], %[[BITCAST]], %[[ARG3]] : vector<8xi1>, vector<8xi4>
+// CHECK-SAME: memref<12xi8>, vector<5xi1>, vector<5xi8> into vector<5xi8>
+// CHECK: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<5xi8> to vector<10xi4>
+// CHECK-COUNT-8: vector.insert {{.+}} : i1 into vector<10xi1>
+// CHECK: %[[SELECT:.+]] = arith.select %{{.+}}, %[[BITCAST]], %{{.+}} : vector<10xi1>, vector<10xi4>
+// CHECK-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// CHECK32-DAG: #[[LOAD_IDX_MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 8)>
-// CHECK32-DAG: #[[MASK_IDX_MAP:.+]] = affine_map<()[s0] -> (s0 ceildiv 8)>
+// CHECK32-DAG: #[[FRONT_MAP:.+]] = affine_map<()[s0] -> (s0 mod 8)>
+// CHECK32-DAG: #[[MASK_IDX_MAP:.+]] = affine_map<()[s0] -> ((s0 + 7) ceildiv 8)>
// CHECK32: func @vector_maskedload_i4(
// CHECK32-SAME: %[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index,
// CHECK32-SAME: %[[ARG2:[a-zA-Z0-9]+]]: index, %[[ARG3:[a-zA-Z0-9]+]]: vector<8xi4>)
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[ORIG_MASK:.+]] = vector.create_mask %[[ARG2]] : vector<8xi1>
// CHECK32: %[[LD_IDX:.+]] = affine.apply #[[LOAD_IDX_MAP]]()[%[[ARG0]], %[[ARG1]]]
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[FRONT_MAP]]()[%[[ARG1]]]
// CHECK32: %[[MASK_IDX:.+]] = affine.apply #[[MASK_IDX_MAP]]()[%[[ARG2]]]
-// CHECK32: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<1xi1>
-// CHECK32: %[[NEW_PASSTHRU:.+]] = vector.bitcast %[[ARG3]] : vector<8xi4> to vector<1xi32>
+// CHECK32: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<2xi1>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i4 into vector<16xi4>
+// CHECK32: %[[NEW_PASSTHRU:.+]] = vector.bitcast %{{.+}} : vector<16xi4> to vector<2xi32>
// CHECK32: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LD_IDX]]], %[[NEW_MASK]], %[[NEW_PASSTHRU]] :
-// CHECK32-SAME: memref<3xi32>, vector<1xi1>, vector<1xi32> into vector<1xi32>
-// CHECK32: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<1xi32> to vector<8xi4>
-// CHECK32: %[[SELECT:.+]] = arith.select %[[ORIG_MASK]], %[[BITCAST]], %[[ARG3]] : vector<8xi1>, vector<8xi4>
+// CHECK32-SAME: memref<3xi32>, vector<2xi1>, vector<2xi32> into vector<2xi32>
+// CHECK32: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<2xi32> to vector<16xi4>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i1 into vector<16xi1>
+// CHECK32: %[[SELECT:.+]] = arith.select %{{.+}}, %[[BITCAST]], %{{.+}} : vector<16xi1>, vector<16xi4>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// -----
@@ -275,20 +355,28 @@ func.func @vector_maskedload_i8_constant_mask(%arg1: index, %arg2: index, %passt
// CHECK-SAME: memref<3x4xi8>, vector<4xi1>, vector<4xi8> into vector<4xi8>
// CHECK-NEXT: return
+// The intra-container offset of the load is dynamic (it depends on %arg2), so
+// one extra container element is loaded (with a pessimistic compressed mask);
+// the mask, the passthru and the result are shifted dynamically by the front
+// offset.
// CHECK32-DAG: #[[LOAD_IDX_MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 4)>
+// CHECK32-DAG: #[[FRONT_MAP:.+]] = affine_map<()[s0] -> (s0 mod 4)>
// CHECK32: func @vector_maskedload_i8_constant_mask(
// CHECK32-SAME: %[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index,
// CHECK32-SAME: %[[ARG3:[a-zA-Z0-9]+]]: vector<4xi8>)
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[ORIG_MASK:.+]] = vector.constant_mask [2] : vector<4xi1>
// CHECK32: %[[LD_IDX:.+]] = affine.apply #[[LOAD_IDX_MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK32: %[[NEW_MASK:.+]] = vector.constant_mask [1] : vector<1xi1>
-// CHECK32: %[[NEW_PASSTHRU:.+]] = vector.bitcast %[[ARG3]] : vector<4xi8> to vector<1xi32>
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[FRONT_MAP]]()[%[[ARG1]]]
+// CHECK32: %[[NEW_MASK:.+]] = vector.constant_mask [2] : vector<2xi1>
+// CHECK32-COUNT-4: vector.insert {{.+}} : i8 into vector<8xi8>
+// CHECK32: %[[NEW_PASSTHRU:.+]] = vector.bitcast %{{.+}} : vector<8xi8> to vector<2xi32>
// CHECK32: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LD_IDX]]], %[[NEW_MASK]], %[[NEW_PASSTHRU]] :
-// CHECK32-SAME: memref<3xi32>, vector<1xi1>, vector<1xi32> into vector<1xi32>
-// CHECK32: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<1xi32> to vector<4xi8>
-// CHECK32: %[[SELECT:.+]] = arith.select %[[ORIG_MASK]], %[[BITCAST]], %[[ARG3]] : vector<4xi1>, vector<4xi8>
-// CHECK32: return %[[SELECT]]
+// CHECK32-SAME: memref<3xi32>, vector<2xi1>, vector<2xi32> into vector<2xi32>
+// CHECK32: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<2xi32> to vector<8xi8>
+// CHECK32-COUNT-4: vector.insert {{.+}} : i1 into vector<8xi1>
+// CHECK32: %[[SELECT:.+]] = arith.select %{{.+}}, %[[BITCAST]], %{{.+}} : vector<8xi1>, vector<8xi8>
+// CHECK32-COUNT-4: vector.insert {{.+}} : i8 into vector<4xi8>
// -----
@@ -301,33 +389,47 @@ func.func @vector_maskedload_i4_constant_mask(%arg1: index, %arg2: index, %passt
%2 = vector.insert %1, %cst [0] : vector<8xi4> into vector<3x8xi4>
return %2 : vector<3x8xi4>
}
+// The intra-container offset of the load is dynamic (it depends on %arg2), so
+// one extra container element is loaded (with a pessimistic compressed mask);
+// the mask, the passthru and the result are shifted dynamically by the front
+// offset.
// CHECK-DAG: #[[LOAD_IDX_MAP:.+]] = affine_map<()[s0, s1] -> (s0 * 4 + s1 floordiv 2)>
+// CHECK-DAG: #[[FRONT_MAP:.+]] = affine_map<()[s0] -> (s0 mod 2)>
// CHECK: func @vector_maskedload_i4_constant_mask(
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index,
// CHECK-SAME: %[[ARG2:[a-zA-Z0-9]+]]: vector<8xi4>)
// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<12xi8>
// CHECK: %[[ORIG_MASK:.+]] = vector.constant_mask [4] : vector<8xi1>
// CHECK: %[[LD_IDX:.+]] = affine.apply #[[LOAD_IDX_MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK: %[[NEW_MASK:.+]] = vector.constant_mask [2] : vector<4xi1>
-// CHECK: %[[NEW_PASSTHRU:.+]] = vector.bitcast %[[ARG2]] : vector<8xi4> to vector<4xi8>
+// CHECK: %[[FRONT:.+]] = affine.apply #[[FRONT_MAP]]()[%[[ARG1]]]
+// CHECK: %[[NEW_MASK:.+]] = vector.constant_mask [3] : vector<5xi1>
+// CHECK-COUNT-8: vector.insert {{.+}} : i4 into vector<10xi4>
+// CHECK: %[[NEW_PASSTHRU:.+]] = vector.bitcast %{{.+}} : vector<10xi4> to vector<5xi8>
// CHECK: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LD_IDX]]], %[[NEW_MASK]], %[[NEW_PASSTHRU]] :
-// CHECK-SAME: memref<12xi8>, vector<4xi1>, vector<4xi8> into vector<4xi8>
-// CHECK: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<4xi8> to vector<8xi4>
-// CHECK: %[[SELECT:.+]] = arith.select %[[ORIG_MASK]], %[[BITCAST]], %[[ARG2]] : vector<8xi1>, vector<8xi4>
+// CHECK-SAME: memref<12xi8>, vector<5xi1>, vector<5xi8> into vector<5xi8>
+// CHECK: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<5xi8> to vector<10xi4>
+// CHECK-COUNT-8: vector.insert {{.+}} : i1 into vector<10xi1>
+// CHECK: %[[SELECT:.+]] = arith.select %{{.+}}, %[[BITCAST]], %{{.+}} : vector<10xi1>, vector<10xi4>
+// CHECK-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// CHECK32-DAG: #[[LOAD_IDX_MAP:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 8)>
+// CHECK32-DAG: #[[FRONT_MAP:.+]] = affine_map<()[s0] -> (s0 mod 8)>
// CHECK32: func @vector_maskedload_i4_constant_mask(
// CHECK32-SAME: %[[ARG0:[a-zA-Z0-9]+]]: index, %[[ARG1:[a-zA-Z0-9]+]]: index,
// CHECK32-SAME: %[[ARG2:[a-zA-Z0-9]+]]: vector<8xi4>)
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[ORIG_MASK:.+]] = vector.constant_mask [4] : vector<8xi1>
// CHECK32: %[[LD_IDX:.+]] = affine.apply #[[LOAD_IDX_MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK32: %[[NEW_MASK:.+]] = vector.constant_mask [1] : vector<1xi1>
-// CHECK32: %[[NEW_PASSTHRU:.+]] = vector.bitcast %[[ARG2]] : vector<8xi4> to vector<1xi32>
+// CHECK32: %[[FRONT:.+]] = affine.apply #[[FRONT_MAP]]()[%[[ARG1]]]
+// CHECK32: %[[NEW_MASK:.+]] = vector.constant_mask [2] : vector<2xi1>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i4 into vector<16xi4>
+// CHECK32: %[[NEW_PASSTHRU:.+]] = vector.bitcast %{{.+}} : vector<16xi4> to vector<2xi32>
// CHECK32: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LD_IDX]]], %[[NEW_MASK]], %[[NEW_PASSTHRU]] :
-// CHECK32-SAME: memref<3xi32>, vector<1xi1>, vector<1xi32> into vector<1xi32>
-// CHECK32: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<1xi32> to vector<8xi4>
-// CHECK32: %[[SELECT:.+]] = arith.select %[[ORIG_MASK]], %[[BITCAST]], %[[ARG2]] : vector<8xi1>, vector<8xi4>
+// CHECK32-SAME: memref<3xi32>, vector<2xi1>, vector<2xi32> into vector<2xi32>
+// CHECK32: %[[BITCAST:.+]] = vector.bitcast %[[LOAD]] : vector<2xi32> to vector<16xi4>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i1 into vector<16xi1>
+// CHECK32: %[[SELECT:.+]] = arith.select %{{.+}}, %[[BITCAST]], %{{.+}} : vector<16xi1>, vector<16xi4>
+// CHECK32-COUNT-8: vector.insert {{.+}} : i4 into vector<8xi4>
// -----
@@ -533,34 +635,36 @@ func.func @vector_store_f4(%arg0: vector<8xf4E2M1FN>, %arg1: index, %arg2: index
/// vector.maskedstore
///----------------------------------------------------------------------------------------
-func.func @vector_maskedstore_i8(%arg0: index, %arg1: index, %arg2: index, %value: vector<8xi8>) {
+// Note: emulating a maskedstore requires a static, container-aligned offset
+// (a dynamic offset makes it impossible to prove alignment and the pattern
+// bails out), hence the constant indices.
+func.func @vector_maskedstore_i8(%arg0: index, %value: vector<8xi8>) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
%0 = memref.alloc() : memref<3x8xi8>
- %mask = vector.create_mask %arg2 : vector<8xi1>
- vector.maskedstore %0[%arg0, %arg1], %mask, %value : memref<3x8xi8>, vector<8xi1>, vector<8xi8>
+ %mask = vector.create_mask %arg0 : vector<8xi1>
+ vector.maskedstore %0[%c1, %c0], %mask, %value : memref<3x8xi8>, vector<8xi1>, vector<8xi8>
return
}
// Expect no conversions, i8 is supported.
// CHECK: func @vector_maskedstore_i8(
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]
-// CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]
-// CHECK-SAME: %[[ARG2:[a-zA-Z0-9]+]]
// CHECK-SAME: %[[VAL:[a-zA-Z0-9]+]]
-// CHECK-NEXT: %[[ALLOC:.+]] = memref.alloc() : memref<3x8xi8>
-// CHECK-NEXT: %[[MASK:.+]] = vector.create_mask %[[ARG2]] : vector<8xi1>
-// CHECK-NEXT: vector.maskedstore %[[ALLOC]][%[[ARG0]], %[[ARG1]]], %[[MASK]], %[[VAL]]
+// CHECK-DAG: %[[C0:.+]] = arith.constant 0 : index
+// CHECK-DAG: %[[C1:.+]] = arith.constant 1 : index
+// CHECK-DAG: %[[ALLOC:.+]] = memref.alloc() : memref<3x8xi8>
+// CHECK: %[[MASK:.+]] = vector.create_mask %[[ARG0]] : vector<8xi1>
+// CHECK-NEXT: vector.maskedstore %[[ALLOC]][%[[C1]], %[[C0]]], %[[MASK]], %[[VAL]]
// CHECK-NEXT: return
-// CHECK32-DAG: #[[LOAD_IDX_MAP:.+]] = affine_map<()[s0, s1] -> (s0 * 2 + s1 floordiv 4)>
// CHECK32-DAG: #[[MASK_IDX_MAP:.+]] = affine_map<()[s0] -> (s0 ceildiv 4)>
// CHECK32: func @vector_maskedstore_i8(
// CHECK32-SAME: %[[ARG0:[a-zA-Z0-9]+]]
-// CHECK32-SAME: %[[ARG1:[a-zA-Z0-9]+]]
-// CHECK32-SAME: %[[ARG2:[a-zA-Z0-9]+]]
// CHECK32-SAME: %[[VAL:[a-zA-Z0-9]+]]
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<6xi32>
-// CHECK32: %[[ORIG_MASK:.+]] = vector.create_mask %[[ARG2]] : vector<8xi1>
-// CHECK32: %[[LIDX:.+]] = affine.apply #[[LOAD_IDX_MAP]]()[%[[ARG0]], %[[ARG1]]]
-// CHECK32: %[[MASK_IDX:.+]] = affine.apply #[[MASK_IDX_MAP]]()[%[[ARG2]]]
+// CHECK32: %[[ORIG_MASK:.+]] = vector.create_mask %[[ARG0]] : vector<8xi1>
+// CHECK32: %[[LIDX:.+]] = arith.constant 2 : index
+// CHECK32: %[[MASK_IDX:.+]] = affine.apply #[[MASK_IDX_MAP]]()[%[[ARG0]]]
// CHECK32: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<2xi1>
// CHECK32: %[[PASS_THRU:.+]] = arith.constant dense<0> : vector<2xi32>
// CHECK32: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LIDX]]], %[[NEW_MASK]], %[[PASS_THRU]]
@@ -571,29 +675,29 @@ func.func @vector_maskedstore_i8(%arg0: index, %arg1: index, %arg2: index, %valu
// -----
+// Note: emulating a maskedstore requires a static, container-aligned offset
+// (a dynamic offset makes it impossible to prove alignment and the pattern
+// bails out), hence the constant indices.
func.func @vector_maskedstore_i4(
- %idx1: index,
- %idx2: index,
%num_elements_to_store: index,
%value: vector<8xi4>) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
%0 = memref.alloc() : memref<3x8xi4>
%mask = vector.create_mask %num_elements_to_store : vector<8xi1>
- vector.maskedstore %0[%idx1, %idx2], %mask, %value :
+ vector.maskedstore %0[%c1, %c0], %mask, %value :
memref<3x8xi4>, vector<8xi1>, vector<8xi4>
return
}
-// CHECK: #[[$ATTR_10:.+]] = affine_map<()[s0, s1] -> (s0 * 4 + s1 floordiv 2)>
// CHECK: #[[$ATTR_11:.+]] = affine_map<()[s0] -> (s0 ceildiv 2)>
// CHECK: func.func @vector_maskedstore_i4(
-// CHECK-SAME: %[[IDX_1:[a-zA-Z0-9]+]]: index,
-// CHECK-SAME: %[[IDX_2:[a-zA-Z0-9]+]]: index,
// CHECK-SAME: %[[NUM_EL_TO_STORE:[a-zA-Z0-9]+]]: index,
// CHECK-SAME: %[[VAL_TO_STORE:[a-zA-Z0-9]+]]: vector<8xi4>) {
// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<12xi8>
// CHECK: %[[ORIG_MASK:.+]] = vector.create_mask %[[NUM_EL_TO_STORE]] : vector<8xi1>
-// CHECK: %[[LIDX:.+]] = affine.apply #[[$ATTR_10]]()[%[[IDX_1]], %[[IDX_2]]]
+// CHECK: %[[LIDX:.+]] = arith.constant 4 : index
// CHECK: %[[MASK_IDX:.+]] = affine.apply #[[$ATTR_11]]()[%[[NUM_EL_TO_STORE]]]
// CHECK: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<4xi1>
// CHECK: %[[PASS_THRU:.+]] = arith.constant dense<0> : vector<4xi8>
@@ -603,17 +707,14 @@ func.func @vector_maskedstore_i4(
// CHECK: %[[NEW_VAL:.+]] = vector.bitcast %[[SELECT]] : vector<8xi4> to vector<4xi8>
// CHECK: vector.maskedstore %[[ALLOC]]{{\[}}%[[LIDX]]], %[[NEW_MASK]], %[[NEW_VAL]] : memref<12xi8>, vector<4xi1>, vector<4xi8>
-// CHECK32: #[[$ATTR_17:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 8)>
// CHECK32: #[[$ATTR_18:.+]] = affine_map<()[s0] -> (s0 ceildiv 8)>
// CHECK32: func.func @vector_maskedstore_i4(
-// CHECK32-SAME: %[[IDX_1:[a-zA-Z0-9]+]]: index,
-// CHECK32-SAME: %[[IDX_2:[a-zA-Z0-9]+]]: index,
// CHECK32-SAME: %[[NUM_EL_TO_STORE:[a-zA-Z0-9]+]]: index,
// CHECK32-SAME: %[[VAL_TO_STORE:[a-zA-Z0-9]+]]: vector<8xi4>) {
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[ORIG_MASK:.+]] = vector.create_mask %[[NUM_EL_TO_STORE]] : vector<8xi1>
-// CHECK32: %[[LIDX:.+]] = affine.apply #[[$ATTR_17]]()[%[[IDX_1]], %[[IDX_2]]]
+// CHECK32: %[[LIDX:.+]] = arith.constant 1 : index
// CHECK32: %[[MASK_IDX:.+]] = affine.apply #[[$ATTR_18]]()[%[[NUM_EL_TO_STORE]]]
// CHECK32: %[[NEW_MASK:.+]] = vector.create_mask %[[MASK_IDX]] : vector<1xi1>
// CHECK32: %[[PASS_THRU:.+]] = arith.constant dense<0> : vector<1xi32>
@@ -625,30 +726,32 @@ func.func @vector_maskedstore_i4(
// -----
-func.func @vector_maskedstore_i8_constant_mask(%arg0: index, %arg1: index, %value: vector<8xi8>) {
+// Note: emulating a maskedstore requires a static, container-aligned offset
+// (a dynamic offset makes it impossible to prove alignment and the pattern
+// bails out), hence the constant indices.
+func.func @vector_maskedstore_i8_constant_mask(%value: vector<8xi8>) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
%0 = memref.alloc() : memref<3x8xi8>
%mask = vector.constant_mask [4] : vector<8xi1>
- vector.maskedstore %0[%arg0, %arg1], %mask, %value : memref<3x8xi8>, vector<8xi1>, vector<8xi8>
+ vector.maskedstore %0[%c1, %c0], %mask, %value : memref<3x8xi8>, vector<8xi1>, vector<8xi8>
return
}
// Expect no conversions, i8 is supported.
// CHECK: func @vector_maskedstore_i8_constant_mask(
-// CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]
-// CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]
// CHECK-SAME: %[[VAL:[a-zA-Z0-9]+]]
-// CHECK-NEXT: %[[ALLOC:.+]] = memref.alloc() : memref<3x8xi8>
-// CHECK-NEXT: %[[MASK:.+]] = vector.constant_mask [4] : vector<8xi1>
-// CHECK-NEXT: vector.maskedstore %[[ALLOC]][%[[ARG0]], %[[ARG1]]], %[[MASK]], %[[VAL]]
+// CHECK-DAG: %[[C0:.+]] = arith.constant 0 : index
+// CHECK-DAG: %[[C1:.+]] = arith.constant 1 : index
+// CHECK-DAG: %[[ALLOC:.+]] = memref.alloc() : memref<3x8xi8>
+// CHECK: %[[MASK:.+]] = vector.constant_mask [4] : vector<8xi1>
+// CHECK-NEXT: vector.maskedstore %[[ALLOC]][%[[C1]], %[[C0]]], %[[MASK]], %[[VAL]]
// CHECK-NEXT: return
-// CHECK32-DAG: #[[LOAD_IDX_MAP:.+]] = affine_map<()[s0, s1] -> (s0 * 2 + s1 floordiv 4)>
// CHECK32: func @vector_maskedstore_i8_constant_mask(
-// CHECK32-SAME: %[[ARG0:[a-zA-Z0-9]+]]
-// CHECK32-SAME: %[[ARG1:[a-zA-Z0-9]+]]
// CHECK32-SAME: %[[VAL:[a-zA-Z0-9]+]]
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<6xi32>
// CHECK32: %[[ORIG_MASK:.+]] = vector.constant_mask [4] : vector<8xi1>
-// CHECK32: %[[LIDX:.+]] = affine.apply #[[LOAD_IDX_MAP]]()[%[[ARG0]], %[[ARG1]]]
+// CHECK32: %[[LIDX:.+]] = arith.constant 2 : index
// CHECK32: %[[NEW_MASK:.+]] = vector.constant_mask [1] : vector<2xi1>
// CHECK32: %[[PASS_THRU:.+]] = arith.constant dense<0> : vector<2xi32>
// CHECK32: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LIDX]]], %[[NEW_MASK]], %[[PASS_THRU]]
@@ -659,26 +762,26 @@ func.func @vector_maskedstore_i8_constant_mask(%arg0: index, %arg1: index, %valu
// -----
+// Note: emulating a maskedstore requires a static, container-aligned offset
+// (a dynamic offset makes it impossible to prove alignment and the pattern
+// bails out), hence the constant indices.
func.func @vector_maskedstore_i4_constant_mask(
- %idx_1: index,
- %idx_2: index,
%val_to_store: vector<8xi4>) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
%0 = memref.alloc() : memref<3x8xi4>
%mask = vector.constant_mask [4] : vector<8xi1>
- vector.maskedstore %0[%idx_1, %idx_2], %mask, %val_to_store :
+ vector.maskedstore %0[%c1, %c0], %mask, %val_to_store :
memref<3x8xi4>, vector<8xi1>, vector<8xi4>
return
}
-// CHECK: #[[$ATTR_12:.+]] = affine_map<()[s0, s1] -> (s0 * 4 + s1 floordiv 2)>
// CHECK: func.func @vector_maskedstore_i4_constant_mask(
-// CHECK-SAME: %[[IDX_1:[a-zA-Z0-9]+]]: index,
-// CHECK-SAME: %[[IDX_2:[a-zA-Z0-9]+]]: index,
// CHECK-SAME: %[[VAL_TO_STORE:[a-zA-Z0-9]+]]: vector<8xi4>) {
// CHECK: %[[ALLOC:.+]] = memref.alloc() : memref<12xi8>
// CHECK: %[[ORIG_MASK:.+]] = vector.constant_mask [4] : vector<8xi1>
-// CHECK: %[[LIDX:.+]] = affine.apply #[[$ATTR_12]]()[%[[IDX_1]], %[[IDX_2]]]
+// CHECK: %[[LIDX:.+]] = arith.constant 4 : index
// CHECK: %[[NEW_MASK:.+]] = vector.constant_mask [2] : vector<4xi1>
// CHECK: %[[PASS_THRU:.+]] = arith.constant dense<0> : vector<4xi8>
// CHECK: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LIDX]]], %[[NEW_MASK]], %[[PASS_THRU]] : memref<12xi8>, vector<4xi1>, vector<4xi8> into vector<4xi8>
@@ -687,14 +790,11 @@ func.func @vector_maskedstore_i4_constant_mask(
// CHECK: %[[NEW_VAL:.+]] = vector.bitcast %[[SELECT]] : vector<8xi4> to vector<4xi8>
// CHECK: vector.maskedstore %[[ALLOC]]{{\[}}%[[LIDX]]], %[[NEW_MASK]], %[[NEW_VAL]] : memref<12xi8>, vector<4xi1>, vector<4xi8>
-// CHECK32: #[[$ATTR_20:.+]] = affine_map<()[s0, s1] -> (s0 + s1 floordiv 8)>
// CHECK32: func.func @vector_maskedstore_i4_constant_mask(
-// CHECK32-SAME: %[[IDX_1:[a-zA-Z0-9]+]]: index,
-// CHECK32-SAME: %[[IDX_2:[a-zA-Z0-9]+]]: index,
// CHECK32-SAME: %[[VAL_TO_STORE:[a-zA-Z0-9]+]]: vector<8xi4>) {
// CHECK32: %[[ALLOC:.+]] = memref.alloc() : memref<3xi32>
// CHECK32: %[[ORIG_MASK:.+]] = vector.constant_mask [4] : vector<8xi1>
-// CHECK32: %[[LIDX:.+]] = affine.apply #[[$ATTR_20]]()[%[[IDX_1]], %[[IDX_2]]]
+// CHECK32: %[[LIDX:.+]] = arith.constant 1 : index
// CHECK32: %[[NEW_MASK:.+]] = vector.constant_mask [1] : vector<1xi1>
// CHECK32: %[[PASS_THRU:.+]] = arith.constant dense<0> : vector<1xi32>
// CHECK32: %[[LOAD:.+]] = vector.maskedload %[[ALLOC]][%[[LIDX]]], %[[NEW_MASK]], %[[PASS_THRU]] : memref<3xi32>, vector<1xi1>, vector<1xi32> into vector<1xi32>
More information about the Mlir-commits
mailing list