Skip to content

Commit 3cc2fb2

Browse files
committed
[mlir][VectorOps] Add vector.interleave operation
The interleave operation constructs a new vector by interleaving the elements from the trailing (or final) dimension of two input vectors, returning a new vector where the trailing dimension is twice the size. Note that for the n-D case this differs from the interleaving possible with `vector.shuffle`, which would only operate on the leading dimension. Another key difference is this operation supports scalable vectors, though currently a general LLVM lowering is limited to the case where only the trailing dimension is scalable. Example: ```mlir %0 = vector.interleave %a, %b : vector<[4]xi32> ; yields vector<[8]xi32> %1 = vector.interleave %c, %d : vector<8xi8> ; yields vector<16xi8> %2 = vector.interleave %e, %f : vector<f16> ; yields vector<2xf16> %3 = vector.interleave %g, %h : vector<2x4x[2]xf64> ; yields vector<2x4x[4]xf64> %4 = vector.interleave %i, %j : vector<6x3xf32> ; yields vector<6x6xf32> ```
1 parent b5c0b67 commit 3cc2fb2

File tree

7 files changed

+330
-1
lines changed

7 files changed

+330
-1
lines changed

mlir/include/mlir/Dialect/Vector/IR/VectorOps.td

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,71 @@ def Vector_ShuffleOp :
478478
let hasCanonicalizer = 1;
479479
}
480480

481+
def Vector_InterleaveOp :
482+
Vector_Op<"interleave", [Pure,
483+
AllTypesMatch<["lhs", "rhs"]>,
484+
TypesMatchWith<
485+
"type of 'result' is double the width of the inputs",
486+
"lhs", "result",
487+
[{
488+
[&]() -> ::mlir::VectorType {
489+
auto vectorType = ::llvm::cast<mlir::VectorType>($_self);
490+
::mlir::VectorType::Builder builder(vectorType);
491+
if (vectorType.getRank() == 0) {
492+
static constexpr int64_t v2xty_shape[] = { 2 };
493+
return builder.setShape(v2xty_shape);
494+
}
495+
auto lastDim = vectorType.getRank() - 1;
496+
return builder.setDim(lastDim, vectorType.getDimSize(lastDim) * 2);
497+
}()
498+
}]>]> {
499+
let summary = "constructs a vector by interleaving two input vectors";
500+
let description = [{
501+
The interleave operation constructs a new vector by interleaving the
502+
elements from the trailing (or final) dimension of two input vectors,
503+
returning a new vector where the trailing dimension is twice the size.
504+
505+
Note that for the n-D case this differs from the interleaving possible with
506+
`vector.shuffle`, which would only operate on the leading dimension.
507+
508+
Another key difference is this operation supports scalable vectors, though
509+
currently a general LLVM lowering is limited to the case where only the
510+
trailing dimension is scalable.
511+
512+
Example:
513+
```mlir
514+
%0 = vector.interleave %a, %b
515+
: vector<[4]xi32> ; yields vector<[8]xi32>
516+
%1 = vector.interleave %c, %d
517+
: vector<8xi8> ; yields vector<16xi8>
518+
%2 = vector.interleave %e, %f
519+
: vector<f16> ; yields vector<2xf16>
520+
%3 = vector.interleave %g, %h
521+
: vector<2x4x[2]xf64> ; yields vector<2x4x[4]xf64>
522+
%4 = vector.interleave %i, %j
523+
: vector<6x3xf32> ; yields vector<6x6xf32>
524+
```
525+
}];
526+
527+
let arguments = (ins AnyVectorOfAnyRank:$lhs, AnyVectorOfAnyRank:$rhs);
528+
let results = (outs AnyVector:$result);
529+
530+
let assemblyFormat = [{
531+
$lhs `,` $rhs attr-dict `:` type($lhs)
532+
}];
533+
534+
let extraClassDeclaration = [{
535+
VectorType getSourceVectorType() {
536+
return ::llvm::cast<VectorType>(getLhs().getType());
537+
}
538+
VectorType getResultVectorType() {
539+
return ::llvm::cast<VectorType>(getResult().getType());
540+
}
541+
}];
542+
543+
let hasCanonicalizer = 1;
544+
}
545+
481546
def Vector_ExtractElementOp :
482547
Vector_Op<"extractelement", [Pure,
483548
TypesMatchWith<"result type matches element type of vector operand",

mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,70 @@ struct VectorSplatNdOpLowering : public ConvertOpToLLVMPattern<SplatOp> {
17341734
}
17351735
};
17361736

1737+
struct VectorInterleaveOpLowering
1738+
: public ConvertOpToLLVMPattern<vector::InterleaveOp> {
1739+
using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern;
1740+
1741+
void initialize() {
1742+
// This pattern recursively unpacks one dimension at a time. The recursion
1743+
// bounded as the rank is strictly decreasing.
1744+
setHasBoundedRewriteRecursion();
1745+
}
1746+
1747+
LogicalResult
1748+
matchAndRewrite(vector::InterleaveOp interleaveOp, OpAdaptor adaptor,
1749+
ConversionPatternRewriter &rewriter) const override {
1750+
VectorType resultType = interleaveOp.getResultVectorType();
1751+
1752+
// If the result is rank 1, then this directly maps to LLVM.
1753+
if (resultType.getRank() == 1) {
1754+
if (resultType.isScalable()) {
1755+
rewriter.replaceOpWithNewOp<LLVM::experimental_vector_interleave2>(
1756+
interleaveOp, typeConverter->convertType(resultType),
1757+
adaptor.getLhs(), adaptor.getRhs());
1758+
return success();
1759+
}
1760+
// Lower fixed-size interleaves to a shufflevector. While the
1761+
// vector.interleave2 intrinsic supports fixed and scalable vectors, the
1762+
// langref still recommends fixed-vectors use shufflevector, see:
1763+
// https://llvm.org/docs/LangRef.html#id876.
1764+
int64_t resultVectorSize = resultType.getNumElements();
1765+
SmallVector<int32_t> interleaveShuffleMask;
1766+
interleaveShuffleMask.reserve(resultVectorSize);
1767+
for (int i = 0; i < resultVectorSize / 2; i++) {
1768+
interleaveShuffleMask.push_back(i);
1769+
interleaveShuffleMask.push_back((resultVectorSize / 2) + i);
1770+
}
1771+
rewriter.replaceOpWithNewOp<LLVM::ShuffleVectorOp>(
1772+
interleaveOp, adaptor.getLhs(), adaptor.getRhs(),
1773+
interleaveShuffleMask);
1774+
return success();
1775+
}
1776+
1777+
// It's not possible to unroll a scalable dimension.
1778+
if (resultType.getScalableDims().front())
1779+
return failure();
1780+
1781+
// n-D case: Unroll the leading dimension.
1782+
// This eventually converges to an LLVM lowering.
1783+
auto loc = interleaveOp.getLoc();
1784+
Value result = rewriter.create<arith::ConstantOp>(
1785+
loc, resultType, rewriter.getZeroAttr(resultType));
1786+
for (int d = 0; d < resultType.getDimSize(0); d++) {
1787+
Value extractLhs =
1788+
rewriter.create<ExtractOp>(loc, interleaveOp.getLhs(), d);
1789+
Value extractRhs =
1790+
rewriter.create<ExtractOp>(loc, interleaveOp.getRhs(), d);
1791+
Value dimInterleave =
1792+
rewriter.create<InterleaveOp>(loc, extractLhs, extractRhs);
1793+
result = rewriter.create<InsertOp>(loc, dimInterleave, result, d);
1794+
}
1795+
1796+
rewriter.replaceOp(interleaveOp, result);
1797+
return success();
1798+
}
1799+
};
1800+
17371801
} // namespace
17381802

17391803
/// Populate the given list with patterns that convert from Vector to LLVM.
@@ -1758,7 +1822,8 @@ void mlir::populateVectorToLLVMConversionPatterns(
17581822
VectorExpandLoadOpConversion, VectorCompressStoreOpConversion,
17591823
VectorSplatOpLowering, VectorSplatNdOpLowering,
17601824
VectorScalableInsertOpLowering, VectorScalableExtractOpLowering,
1761-
MaskedReductionOpConversion>(converter);
1825+
MaskedReductionOpConversion, VectorInterleaveOpLowering>(
1826+
converter);
17621827
// Transfer ops with rank > 1 are handled by VectorToSCF.
17631828
populateVectorTransferLoweringPatterns(patterns, /*maxTransferRank=*/1);
17641829
}

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6308,6 +6308,48 @@ bool WarpExecuteOnLane0Op::areTypesCompatible(Type lhs, Type rhs) {
63086308
verifyDistributedType(lhs, rhs, getWarpSize(), getOperation()));
63096309
}
63106310

6311+
//===----------------------------------------------------------------------===//
6312+
// InterleaveOp
6313+
//===----------------------------------------------------------------------===//
6314+
6315+
// The rank 1 case of vector.interleave on fixed-size vectors is equivalent to a
6316+
// vector.shuffle, which (as an older op) is more likely to be matched by
6317+
// existing pipelines.
6318+
struct FoldRank1FixedSizeInterleaveOp : public OpRewritePattern<InterleaveOp> {
6319+
using OpRewritePattern::OpRewritePattern;
6320+
6321+
LogicalResult matchAndRewrite(InterleaveOp interleaveOp,
6322+
PatternRewriter &rewriter) const override {
6323+
auto resultType = interleaveOp.getResultVectorType();
6324+
if (resultType.getRank() != 1)
6325+
return rewriter.notifyMatchFailure(
6326+
interleaveOp, "cannot fold interleave with result rank > 1");
6327+
6328+
if (resultType.isScalable())
6329+
return rewriter.notifyMatchFailure(
6330+
interleaveOp, "cannot fold interleave of scalable vectors");
6331+
6332+
int64_t resultVectorSize = resultType.getNumElements();
6333+
SmallVector<int64_t> interleaveShuffleMask;
6334+
interleaveShuffleMask.reserve(resultVectorSize);
6335+
for (int i = 0; i < resultVectorSize / 2; i++) {
6336+
interleaveShuffleMask.push_back(i);
6337+
interleaveShuffleMask.push_back((resultVectorSize / 2) + i);
6338+
}
6339+
6340+
rewriter.replaceOpWithNewOp<ShuffleOp>(interleaveOp, interleaveOp.getLhs(),
6341+
interleaveOp.getRhs(),
6342+
interleaveShuffleMask);
6343+
6344+
return success();
6345+
}
6346+
};
6347+
6348+
void InterleaveOp::getCanonicalizationPatterns(RewritePatternSet &results,
6349+
MLIRContext *context) {
6350+
results.add<FoldRank1FixedSizeInterleaveOp>(context);
6351+
}
6352+
63116353
Value mlir::vector::makeArithReduction(OpBuilder &b, Location loc,
63126354
CombiningKind kind, Value v1, Value acc,
63136355
arith::FastMathFlagsAttr fastmath,

mlir/test/Conversion/VectorToLLVM/vector-to-llvm.mlir

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,3 +2460,88 @@ func.func @make_fixed_vector_of_scalable_vector(%f : f64) -> vector<3x[2]xf64>
24602460
%res = vector.broadcast %f : f64 to vector<3x[2]xf64>
24612461
return %res : vector<3x[2]xf64>
24622462
}
2463+
2464+
// -----
2465+
2466+
// CHECK-LABEL: @vector_interleave_0d
2467+
// CHECK-SAME: %[[LHS:.*]]: vector<i8>, %[[RHS:.*]]: vector<i8>)
2468+
func.func @vector_interleave_0d(%a: vector<i8>, %b: vector<i8>) -> vector<2xi8> {
2469+
// CHECK: %[[LHS_RANK1:.*]] = builtin.unrealized_conversion_cast %[[LHS]] : vector<i8> to vector<1xi8>
2470+
// CHECK: %[[RHS_RANK1:.*]] = builtin.unrealized_conversion_cast %[[RHS]] : vector<i8> to vector<1xi8>
2471+
// CHECK: %[[ZIP:.*]] = llvm.shufflevector %[[LHS_RANK1]], %[[RHS_RANK1]] [0, 1] : vector<1xi8>
2472+
// CHECK: return %[[ZIP]]
2473+
%0 = vector.interleave %a, %b : vector<i8>
2474+
return %0 : vector<2xi8>
2475+
}
2476+
2477+
// -----
2478+
2479+
// CHECK-LABEL: @vector_interleave_1d
2480+
// CHECK-SAME: %[[LHS:.*]]: vector<8xf32>, %[[RHS:.*]]: vector<8xf32>)
2481+
func.func @vector_interleave_1d(%a: vector<8xf32>, %b: vector<8xf32>) -> vector<16xf32>
2482+
{
2483+
// CHECK: %[[ZIP:.*]] = llvm.shufflevector %[[LHS]], %[[RHS]] [0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15] : vector<8xf32>
2484+
// CHECK: return %[[ZIP]]
2485+
%0 = vector.interleave %a, %b : vector<8xf32>
2486+
return %0 : vector<16xf32>
2487+
}
2488+
2489+
// -----
2490+
2491+
// CHECK-LABEL: @vector_interleave_1d_scalable
2492+
// CHECK-SAME: %[[LHS:.*]]: vector<[4]xi32>, %[[RHS:.*]]: vector<[4]xi32>)
2493+
func.func @vector_interleave_1d_scalable(%a: vector<[4]xi32>, %b: vector<[4]xi32>) -> vector<[8]xi32>
2494+
{
2495+
// CHECK: %[[ZIP:.*]] = "llvm.intr.experimental.vector.interleave2"(%[[LHS]], %[[RHS]]) : (vector<[4]xi32>, vector<[4]xi32>) -> vector<[8]xi32>
2496+
// CHECK: return %[[ZIP]]
2497+
%0 = vector.interleave %a, %b : vector<[4]xi32>
2498+
return %0 : vector<[8]xi32>
2499+
}
2500+
2501+
// -----
2502+
2503+
// CHECK-LABEL: @vector_interleave_2d
2504+
// CHECK-SAME: %[[LHS:.*]]: vector<2x3xi8>, %[[RHS:.*]]: vector<2x3xi8>)
2505+
func.func @vector_interleave_2d(%a: vector<2x3xi8>, %b: vector<2x3xi8>) -> vector<2x6xi8>
2506+
{
2507+
// CHECK: %[[LHS_LLVM:.*]] = builtin.unrealized_conversion_cast %[[LHS]] : vector<2x3xi8> to !llvm.array<2 x vector<3xi8>>
2508+
// CHECK: %[[RHS_LLVM:.*]] = builtin.unrealized_conversion_cast %[[RHS]] : vector<2x3xi8> to !llvm.array<2 x vector<3xi8>>
2509+
// CHECK: %[[CST:.*]] = arith.constant dense<0> : vector<2x6xi8>
2510+
// CHECK: %[[CST_LLVM:.*]] = builtin.unrealized_conversion_cast %[[CST]] : vector<2x6xi8> to !llvm.array<2 x vector<6xi8>>
2511+
// CHECK: %[[LHS_DIM_0:.*]] = llvm.extractvalue %[[LHS_LLVM]][0] : !llvm.array<2 x vector<3xi8>>
2512+
// CHECK: %[[RHS_DIM_0:.*]] = llvm.extractvalue %[[RHS_LLVM]][0] : !llvm.array<2 x vector<3xi8>>
2513+
// CHECK: %[[ZIM_DIM_0:.*]] = llvm.shufflevector %[[LHS_DIM_0]], %[[RHS_DIM_0]] [0, 3, 1, 4, 2, 5] : vector<3xi8>
2514+
// CHECK: %[[RES_0:.*]] = llvm.insertvalue %[[ZIM_DIM_0]], %[[CST_LLVM]][0] : !llvm.array<2 x vector<6xi8>>
2515+
// CHECK: %[[LHS_DIM_1:.*]] = llvm.extractvalue %[[LHS_LLVM]][1] : !llvm.array<2 x vector<3xi8>>
2516+
// CHECK: %[[RHS_DIM_1:.*]] = llvm.extractvalue %[[RHS_LLVM]][1] : !llvm.array<2 x vector<3xi8>>
2517+
// CHECK: %[[ZIM_DIM_1:.*]] = llvm.shufflevector %[[LHS_DIM_1]], %[[RHS_DIM_1]] [0, 3, 1, 4, 2, 5] : vector<3xi8>
2518+
// CHECK: %[[RES_1:.*]] = llvm.insertvalue %[[ZIM_DIM_1]], %[[RES_0]][1] : !llvm.array<2 x vector<6xi8>>
2519+
// CHECK: %[[RES:.*]] = builtin.unrealized_conversion_cast %[[RES_1]] : !llvm.array<2 x vector<6xi8>> to vector<2x6xi8>
2520+
// CHECK: return %[[RES]]
2521+
%0 = vector.interleave %a, %b : vector<2x3xi8>
2522+
return %0 : vector<2x6xi8>
2523+
}
2524+
2525+
// -----
2526+
2527+
// CHECK-LABEL: @vector_interleave_2d_scalable
2528+
// CHECK-SAME: %[[LHS:.*]]: vector<2x[8]xi16>, %[[RHS:.*]]: vector<2x[8]xi16>)
2529+
func.func @vector_interleave_2d_scalable(%a: vector<2x[8]xi16>, %b: vector<2x[8]xi16>) -> vector<2x[16]xi16>
2530+
{
2531+
// CHECK: %[[LHS_LLVM:.*]] = builtin.unrealized_conversion_cast %arg0 : vector<2x[8]xi16> to !llvm.array<2 x vector<[8]xi16>>
2532+
// CHECK: %[[RHS_LLVM:.*]] = builtin.unrealized_conversion_cast %arg1 : vector<2x[8]xi16> to !llvm.array<2 x vector<[8]xi16>>
2533+
// CHECK: %[[CST:.*]] = arith.constant dense<0> : vector<2x[16]xi16>
2534+
// CHECK: %[[CST_LLVM:.*]] = builtin.unrealized_conversion_cast %[[CST]] : vector<2x[16]xi16> to !llvm.array<2 x vector<[16]xi16>>
2535+
// CHECK: %[[LHS_DIM_0:.*]] = llvm.extractvalue %[[LHS_LLVM]][0] : !llvm.array<2 x vector<[8]xi16>>
2536+
// CHECK: %[[RHS_DIM_0:.*]] = llvm.extractvalue %[[RHS_LLVM]][0] : !llvm.array<2 x vector<[8]xi16>>
2537+
// CHECK: %[[ZIM_DIM_0:.*]] = "llvm.intr.experimental.vector.interleave2"(%[[LHS_DIM_0]], %[[RHS_DIM_0]]) : (vector<[8]xi16>, vector<[8]xi16>) -> vector<[16]xi16>
2538+
// CHECK: %[[RES_0:.*]] = llvm.insertvalue %[[ZIM_DIM_0]], %[[CST_LLVM]][0] : !llvm.array<2 x vector<[16]xi16>>
2539+
// CHECK: %[[LHS_DIM_1:.*]] = llvm.extractvalue %0[1] : !llvm.array<2 x vector<[8]xi16>>
2540+
// CHECK: %[[RHS_DIM_1:.*]] = llvm.extractvalue %1[1] : !llvm.array<2 x vector<[8]xi16>>
2541+
// CHECK: %[[ZIP_DIM_1:.*]] = "llvm.intr.experimental.vector.interleave2"(%[[LHS_DIM_1]], %[[RHS_DIM_1]]) : (vector<[8]xi16>, vector<[8]xi16>) -> vector<[16]xi16>
2542+
// CHECK: %[[RES_1:.*]] = llvm.insertvalue %[[ZIP_DIM_1]], %[[RES_0]][1] : !llvm.array<2 x vector<[16]xi16>>
2543+
// CHECK: %[[RES:.*]] = builtin.unrealized_conversion_cast %[[RES_1]] : !llvm.array<2 x vector<[16]xi16>> to vector<2x[16]xi16>
2544+
// CHECK: return %[[RES]]
2545+
%0 = vector.interleave %a, %b : vector<2x[8]xi16>
2546+
return %0 : vector<2x[16]xi16>
2547+
}

mlir/test/Dialect/Vector/canonicalize.mlir

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,3 +2567,26 @@ func.func @load_store_forwarding_rank_mismatch(%v0: vector<4x1x1xf32>, %arg0: te
25672567
tensor<4x4x4xf32>, vector<1x100x4x5xf32>
25682568
return %r : vector<1x100x4x5xf32>
25692569
}
2570+
2571+
// -----
2572+
2573+
// CHECK-LABEL: func.func @fold_rank_1_vector_interleave(
2574+
// CHECK-SAME: %[[LHS:.*]]: vector<6xi32>, %[[RHS:.*]]: vector<6xi32>)
2575+
func.func @fold_rank_1_vector_interleave(%arg0: vector<6xi32>, %arg1: vector<6xi32>) -> vector<12xi32> {
2576+
// CHECK: %[[ZIP:.*]] = vector.shuffle %[[LHS]], %[[RHS]] [0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11] : vector<6xi32>, vector<6xi32>
2577+
// CHECK: return %[[ZIP]] : vector<12xi32>
2578+
%0 = vector.interleave %arg0, %arg1 : vector<6xi32>
2579+
return %0 : vector<12xi32>
2580+
}
2581+
2582+
// -----
2583+
2584+
// CHECK-LABEL: func.func @fold_rank_0_vector_interleave(
2585+
// CHECK-SAME: %[[LHS:.*]]: vector<f64>, %[[RHS:.*]]: vector<f64>)
2586+
func.func @fold_rank_0_vector_interleave(%arg0: vector<f64>, %arg1: vector<f64>) -> vector<2xf64>
2587+
{
2588+
// CHECK: %[[ZIP:.*]] = vector.shuffle %[[LHS]], %[[RHS]] [0, 1] : vector<f64>, vector<f64>
2589+
// CHECK: return %[[ZIP]] : vector<2xf64>
2590+
%0 = vector.interleave %arg0, %arg1 : vector<f64>
2591+
return %0 : vector<2xf64>
2592+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: mlir-opt %s -test-lower-to-llvm | \
2+
// RUN: %mcr_aarch64_cmd -e entry -entry-point-result=void \
3+
// RUN: -shared-libs=%mlir_c_runner_utils,%mlir_arm_runner_utils | \
4+
// RUN: FileCheck %s
5+
6+
func.func @entry() {
7+
%f1 = arith.constant 1.0: f32
8+
%f2 = arith.constant 2.0: f32
9+
%v1 = vector.splat %f1 : vector<[4]xf32>
10+
%v2 = vector.splat %f2 : vector<[4]xf32>
11+
vector.print %v1 : vector<[4]xf32>
12+
vector.print %v2 : vector<[4]xf32>
13+
//
14+
// Test vectors:
15+
//
16+
// CHECK: ( 1, 1, 1, 1
17+
// CHECK: ( 2, 2, 2, 2
18+
19+
%v3 = vector.interleave %v1, %v2 : vector<[4]xf32>
20+
vector.print %v3 : vector<[8]xf32>
21+
// CHECK: ( 1, 2, 1, 2, 1, 2, 1, 2
22+
23+
return
24+
}
25+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: mlir-opt %s -test-lower-to-llvm | \
2+
// RUN: mlir-cpu-runner -e entry -entry-point-result=void \
3+
// RUN: -shared-libs=%mlir_c_runner_utils | \
4+
// RUN: FileCheck %s
5+
6+
func.func @entry() {
7+
%f1 = arith.constant 1.0: f32
8+
%f2 = arith.constant 2.0: f32
9+
%v1 = vector.splat %f1 : vector<2x4xf32>
10+
%v2 = vector.splat %f2 : vector<2x4xf32>
11+
vector.print %v1 : vector<2x4xf32>
12+
vector.print %v2 : vector<2x4xf32>
13+
//
14+
// Test vectors:
15+
//
16+
// CHECK: ( ( 1, 1, 1, 1 ), ( 1, 1, 1, 1 ) )
17+
// CHECK: ( ( 2, 2, 2, 2 ), ( 2, 2, 2, 2 ) )
18+
19+
%v3 = vector.interleave %v1, %v2 : vector<2x4xf32>
20+
vector.print %v3 : vector<2x8xf32>
21+
// CHECK: ( ( 1, 2, 1, 2, 1, 2, 1, 2 ), ( 1, 2, 1, 2, 1, 2, 1, 2 ) )
22+
23+
return
24+
}

0 commit comments

Comments
 (0)