[mlir][Linalg] Rethink fusion of linalg ops with reshape ops.
The current fusion on tensors fuses reshape ops with generic ops by linearizing the indexing maps of the fused tensor in the generic op. This has some limitations - It only works for static shapes - The resulting indexing map has a linearization that would be potentially prevent fusion later on (for ex. tile + fuse). Instead, try to fuse the reshape consumer (producer) with generic op producer (consumer) by expanding the dimensionality of the generic op when the reshape is expanding (folding). This approach conflicts with the linearization approach. The expansion method is used instead of the linearization method. Further refactoring that changes the fusion on tensors to be a collection of patterns. Differential Revision: https://reviews.llvm.org/D89002
This commit is contained in:
@@ -85,7 +85,7 @@ class Linalg_ReshapeLikeOp<string mnemonic, list<OpTrait> traits = []> :
|
||||
"ArrayRef<NamedAttribute> attrs = {}", [{
|
||||
auto reassociationMaps =
|
||||
convertReassociationIndicesToMaps($_builder, reassociation);
|
||||
build($_builder, $_state, src, reassociationMaps, attrs);
|
||||
build($_builder, $_state, resultType, src, reassociationMaps, attrs);
|
||||
}]>
|
||||
];
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ std::unique_ptr<OperationPass<FuncOp>> createLinalgFoldUnitExtentDimsPass();
|
||||
|
||||
std::unique_ptr<OperationPass<FuncOp>> createLinalgFusionPass();
|
||||
std::unique_ptr<Pass> createLinalgFusionOfTensorOpsPass();
|
||||
std::unique_ptr<Pass> createFoldReshapeOpsByLinearizationPass();
|
||||
|
||||
std::unique_ptr<OperationPass<FuncOp>>
|
||||
createLinalgTilingPass(ArrayRef<int64_t> tileSizes = {});
|
||||
@@ -48,6 +49,19 @@ std::unique_ptr<OperationPass<FuncOp>> createConvertLinalgToAffineLoopsPass();
|
||||
/// buffers instead.
|
||||
std::unique_ptr<OperationPass<ModuleOp>> createLinalgBufferizePass();
|
||||
|
||||
/// Patterns to fold an expanding (collapsing) tensor_reshape operation with its
|
||||
/// producer (consumer) generic operation by expanding the dimensionality of the
|
||||
/// loop in the generic op.
|
||||
void populateFoldReshapeOpsByExpansionPatterns(
|
||||
MLIRContext *context, OwningRewritePatternList &patterns);
|
||||
|
||||
/// Patterns to fold a collapsing (expanding) tensor_reshape operation with its
|
||||
/// producer (consumer) generic/indexed_generic operation by linearizing the
|
||||
/// indexing map used to access the source (target) of the reshape operation in
|
||||
/// the generic/indexed_generic operation.
|
||||
void populateFoldReshapeOpsByLinearizationPatterns(
|
||||
MLIRContext *context, OwningRewritePatternList &patterns);
|
||||
|
||||
/// Patterns for fusing linalg operation on tensors.
|
||||
void populateLinalgTensorOpsFusionPatterns(MLIRContext *context,
|
||||
OwningRewritePatternList &patterns);
|
||||
|
||||
@@ -35,6 +35,14 @@ def LinalgFusionOfTensorOps : Pass<"linalg-fusion-for-tensor-ops"> {
|
||||
let dependentDialects = ["linalg::LinalgDialect", "AffineDialect"];
|
||||
}
|
||||
|
||||
def LinalgFoldReshapeOpsByLinearization :
|
||||
Pass<"linalg-fold-reshape-ops-by-linearization"> {
|
||||
let summary = "Fold TensorReshapeOps with generic/indexed generic ops by "
|
||||
"linearization";
|
||||
let constructor = "mlir::createFoldReshapeOpsByLinearizationPass()";
|
||||
let dependentDialects = ["AffineDialect"];
|
||||
}
|
||||
|
||||
def LinalgLowerToAffineLoops : FunctionPass<"convert-linalg-to-affine-loops"> {
|
||||
let summary = "Lower the operations from the linalg dialect into affine "
|
||||
"loops";
|
||||
|
||||
@@ -91,9 +91,9 @@ Optional<FusionInfo> fuseProducerOf(OpBuilder &b, LinalgOp consumer,
|
||||
|
||||
/// Fuse linalg operation on tensors, with the producer of the operand at
|
||||
/// position `consumerIdx` of the consumer.
|
||||
Operation *fuseTensorOps(PatternRewriter &rewriter, Operation *consumer,
|
||||
unsigned consumerIdx,
|
||||
OperationFolder *folder = nullptr);
|
||||
Optional<SmallVector<Value, 1>>
|
||||
fuseTensorOps(PatternRewriter &rewriter, Operation *consumer,
|
||||
unsigned consumerIdx, OperationFolder *folder = nullptr);
|
||||
|
||||
/// Returns the linearized list of all shape dimensions in a `linalgOp`.
|
||||
/// Applying the inverse, concatenated loopToOperandRangeMaps to this list
|
||||
|
||||
@@ -514,9 +514,8 @@ struct CollapseReshapeOps : public OpRewritePattern<ReshapeOpTy> {
|
||||
return success();
|
||||
}
|
||||
// Check if producer and consumer are both collapsing dims.
|
||||
else if (areReshapeOpsFoldable(srcReshapeOp.getSrcType(),
|
||||
reshapeOp.getSrcType(),
|
||||
reshapeOp.getResultType())) {
|
||||
if (areReshapeOpsFoldable(srcReshapeOp.getSrcType(), reshapeOp.getSrcType(),
|
||||
reshapeOp.getResultType())) {
|
||||
rewriter.replaceOpWithNewOp<ReshapeOpTy>(
|
||||
reshapeOp, reshapeOp.getResultType(), srcReshapeOp.src(),
|
||||
collapseReassociationMaps(srcReshapeOp.getReassociationMaps(),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -142,124 +142,6 @@ func @add_mul_scalar_fusion(%arg0: tensor<f32>, %arg1: tensor<f32>, %arg2: tenso
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1 * 4 + d2, d3)>
|
||||
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @generic_op_reshape_producer_fusion(%arg0 : tensor<?x?x?xf32>,
|
||||
%arg1 : tensor<?x?x4x?xf32>) ->
|
||||
tensor<?x?x4x?xf32>
|
||||
{
|
||||
%0 = linalg.tensor_reshape %arg0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k)>,
|
||||
affine_map<(i, j, k, l) -> (l)>] :
|
||||
tensor<?x?x?xf32> into tensor<?x?x4x?xf32>
|
||||
%1 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"]}
|
||||
ins(%0, %arg1 : tensor<?x?x4x?xf32>, tensor<?x?x4x?xf32>) {
|
||||
^bb0(%arg3: f32, %arg4: f32): // no predecessors
|
||||
%1 = mulf %arg3, %arg4 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?x4x?xf32>
|
||||
return %1 : tensor<?x?x4x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_reshape_producer_fusion
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.generic
|
||||
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1 * 20 + d2 * 5 + d3)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @generic_op_reshape_consumer_fusion(%arg0 : tensor<?x?x4x5xf32>,
|
||||
%arg1 : tensor<?x?x4x5xf32>) ->
|
||||
tensor<?x?xf32>
|
||||
{
|
||||
%0 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"]}
|
||||
ins(%arg0, %arg1 : tensor<?x?x4x5xf32>, tensor<?x?x4x5xf32>) {
|
||||
^bb0(%arg3: f32, %arg4: f32): // no predecessors
|
||||
%1 = mulf %arg3, %arg4 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?x4x5xf32>
|
||||
%1 = linalg.tensor_reshape %0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k, l)>] :
|
||||
tensor<?x?x4x5xf32> into tensor<?x?xf32>
|
||||
return %1 : tensor<?x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_reshape_consumer_fusion
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.generic
|
||||
|
||||
// -----
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @generic_op_reshape_consumer_nofusion(%arg0 : tensor<?x?x?x5xf32>,
|
||||
%arg1 : tensor<?x?x?x5xf32>) ->
|
||||
tensor<?x?xf32>
|
||||
{
|
||||
%0 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"]}
|
||||
ins(%arg0, %arg1 : tensor<?x?x?x5xf32>, tensor<?x?x?x5xf32>) {
|
||||
^bb0(%arg3: f32, %arg4: f32): // no predecessors
|
||||
%1 = mulf %arg3, %arg4 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?x?x5xf32>
|
||||
%1 = linalg.tensor_reshape %0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k, l)>] :
|
||||
tensor<?x?x?x5xf32> into tensor<?x?xf32>
|
||||
return %1 : tensor<?x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_reshape_consumer_nofusion
|
||||
// CHECK: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
#map0 = affine_map<(d0, d1) -> (d0, d1)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d0, d1)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d2)>
|
||||
|
||||
func @generic_op_reshape_consumer_expanding(%arg0: tensor<264x4xf32>)
|
||||
-> tensor<8x33x4xf32> {
|
||||
%cst = constant dense<2.000000e+00> : tensor<264x4xf32>
|
||||
%0 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel"]}
|
||||
ins(%arg0, %cst : tensor<264x4xf32>, tensor<264x4xf32>) {
|
||||
^bb0(%arg1: f32, %arg2: f32): // no predecessors
|
||||
%2 = mulf %arg1, %arg2 : f32
|
||||
linalg.yield %2 : f32
|
||||
} -> tensor<264x4xf32>
|
||||
%1 = linalg.tensor_reshape %0 [#map1, #map2] :
|
||||
tensor<264x4xf32> into tensor<8x33x4xf32>
|
||||
return %1 : tensor<8x33x4xf32>
|
||||
}
|
||||
|
||||
// The reshape op in `%arg0` is folded into the indexing map of generic op.
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d0 * 33 + d1, d2)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
// CHECK: func @generic_op_reshape_consumer_expanding
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: %[[CST:.*]] = constant {{.*}} : f32
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[MAP0]], #[[MAP1]]]
|
||||
// CHECK-SAME: tensor<264x4xf32>
|
||||
// CHECK: -> tensor<8x33x4xf32>
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
func @generic_op_constant_fusion(%arg0 : tensor<5x?x?xf32>) -> tensor<5x?x?xf32>
|
||||
@@ -499,159 +381,3 @@ func @indexed_generic_op_fusion(%arg0: tensor<?x?xi32>) {
|
||||
// CHECK: %[[VAL4:.+]] = subi %[[VAL3]], %[[SUB_OPERAND2]] : i32
|
||||
// CHECK: linalg.yield %[[VAL4]] : i32
|
||||
// CHECK-NOT: linalg.indexed_generic
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1 * 4 + d2, d3)>
|
||||
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @indexed_generic_op_reshape_producer_fusion(%arg0 : tensor<?x?x?xi32>)
|
||||
-> tensor<?x?x4x?xi32> {
|
||||
%0 = linalg.tensor_reshape %arg0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k)>,
|
||||
affine_map<(i, j, k, l) -> (l)>] :
|
||||
tensor<?x?x?xi32> into tensor<?x?x4x?xi32>
|
||||
%1 = linalg.indexed_generic {
|
||||
indexing_maps = [#map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"] }
|
||||
ins(%0 : tensor<?x?x4x?xi32>) {
|
||||
^bb0(%arg2: index, %arg3: index, %arg4: index, %arg5: index, %arg6: i32): // no predecessors
|
||||
%2 = index_cast %arg2 : index to i32
|
||||
%3 = addi %arg6, %2 : i32
|
||||
linalg.yield %3 : i32
|
||||
} -> tensor<?x?x4x?xi32>
|
||||
return %1 : tensor<?x?x4x?xi32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @indexed_generic_op_reshape_producer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.indexed_generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1 * 20 + d2 * 5 + d3)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @indexed_generic_op_reshape_consumer_fusion(%arg0 : tensor<?x?x4x5xi32>)
|
||||
-> tensor<?x?xi32> {
|
||||
%0 = linalg.indexed_generic {
|
||||
indexing_maps = [#map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"] }
|
||||
ins(%arg0 : tensor<?x?x4x5xi32>) {
|
||||
^bb0(%arg2: index, %arg3: index, %arg4: index, %arg5: index, %arg6: i32): // no predecessors
|
||||
%2 = index_cast %arg2 : index to i32
|
||||
%3 = addi %arg6, %2 : i32
|
||||
linalg.yield %3 : i32
|
||||
} -> tensor<?x?x4x5xi32>
|
||||
%1 = linalg.tensor_reshape %0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k, l)>] :
|
||||
tensor<?x?x4x5xi32> into tensor<?x?xi32>
|
||||
return %1 : tensor<?x?xi32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @indexed_generic_op_reshape_consumer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.indexed_generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d0, d1 + d2 * 7)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d0, d2, d1)>
|
||||
#map3 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
func @generic_op_021_permultation_reshape_producer_fusion(%arg0 : tensor<3x35xf32>) -> tensor<3x7x5xf32> {
|
||||
%0 = linalg.tensor_reshape %arg0 [#map0, #map1] : tensor<3x35xf32> into tensor<3x5x7xf32>
|
||||
%1 = linalg.generic {indexing_maps = [#map2, #map3], iterator_types = ["parallel", "parallel", "parallel"]} ins(%0 : tensor<3x5x7xf32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<3x7x5xf32>
|
||||
return %1 : tensor<3x7x5xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_021_permultation_reshape_producer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d2, d0 * 7 + d1)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d1, d2, d0)>
|
||||
#map3 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
func @generic_op_120_permultation_reshape_producer_fusion(%arg0 : tensor<3x35xf32>) -> tensor<5x7x3xf32> {
|
||||
%0 = linalg.tensor_reshape %arg0 [#map0, #map1] : tensor<3x35xf32> into tensor<3x5x7xf32>
|
||||
%1 = linalg.generic {indexing_maps = [#map2, #map3], iterator_types = ["parallel", "parallel", "parallel"]} ins(%0 : tensor<3x5x7xf32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<5x7x3xf32>
|
||||
return %1 : tensor<5x7x3xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_120_permultation_reshape_producer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d1, d0 * 7 + d2)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d1, d0, d2)>
|
||||
#map3 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
func @generic_op_102_permultation_reshape_producer_fusion(%arg0 : tensor<3x35xf32>) -> tensor<5x3x7xf32> {
|
||||
%0 = linalg.tensor_reshape %arg0 [#map0, #map1] : tensor<3x35xf32> into tensor<3x5x7xf32>
|
||||
%1 = linalg.generic {indexing_maps = [#map2, #map3], iterator_types = ["parallel", "parallel", "parallel"]} ins(%0 : tensor<3x5x7xf32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<5x3x7xf32>
|
||||
return %1 : tensor<5x3x7xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_102_permultation_reshape_producer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d1, d0 * 7 + d2)>
|
||||
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d0, d2)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map3 = affine_map<(d0, d1, d2) -> (d1, d2)>
|
||||
func @generic_op_102_permultation_reshape_consumer_fusion(%arg0 : tensor<3x5x7xf32>) -> tensor<5x21xf32> {
|
||||
%0 = linalg.generic {indexing_maps = [#map0, #map1], iterator_types = ["parallel", "parallel", "parallel"]} ins(%arg0 : tensor<3x5x7xf32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<5x3x7xf32>
|
||||
%1 = linalg.tensor_reshape %0 [#map2, #map3] : tensor<5x3x7xf32> into tensor<5x21xf32>
|
||||
return %1 : tensor<5x21xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_102_permultation_reshape_consumer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
192
mlir/test/Dialect/Linalg/reshape_fusion.mlir
Normal file
192
mlir/test/Dialect/Linalg/reshape_fusion.mlir
Normal file
@@ -0,0 +1,192 @@
|
||||
// RUN: mlir-opt %s -linalg-fusion-for-tensor-ops -split-input-file | FileCheck %s
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d2, d0, d1)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d2, d0)>
|
||||
func @generic_op_reshape_producer_fusion(%arg0 : tensor<?x?x?x?xf32>,
|
||||
%arg1 : tensor<?x?x?xf32>) ->
|
||||
tensor<?x?x?xf32>
|
||||
{
|
||||
%0 = linalg.tensor_reshape %arg0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k)>,
|
||||
affine_map<(i, j, k, l) -> (l)>] :
|
||||
tensor<?x?x?x?xf32> into tensor<?x?x?xf32>
|
||||
%1 = linalg.generic {
|
||||
indexing_maps = [#map0, #map1, #map1],
|
||||
iterator_types = ["parallel", "parallel", "parallel"]}
|
||||
ins(%0, %arg1 : tensor<?x?x?xf32>, tensor<?x?x?xf32>) {
|
||||
^bb0(%arg3: f32, %arg4: f32): // no predecessors
|
||||
%1 = mulf %arg3, %arg4 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?x?xf32>
|
||||
return %1 : tensor<?x?x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2, d3) -> (d0)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2, d3) -> (d1)>
|
||||
// CHECK-DAG: #[[MAP2:.+]] = affine_map<(d0, d1, d2, d3) -> (d2, d3)>
|
||||
// CHECK-DAG: #[[MAP3:.+]] = affine_map<(d0, d1, d2, d3) -> (d3, d0, d1, d2)>
|
||||
// CHECK-DAG: #[[MAP4:.+]] = affine_map<(d0, d1, d2, d3) -> (d2, d3, d0, d1)>
|
||||
// CHECK: func @generic_op_reshape_producer_fusion
|
||||
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9_]+]]: tensor<?x?x?x?xf32>
|
||||
// CHECK-SAME: %[[ARG1:[a-zA-Z0-9_]+]]: tensor<?x?x?xf32>
|
||||
// CHECK: %[[T0:.+]] = linalg.tensor_reshape %[[ARG1]]
|
||||
// CHECK-SAME: [#[[MAP0]], #[[MAP1]], #[[MAP2]]]
|
||||
// CHECK-SAME: tensor<?x?x?xf32> into tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[T1:.+]] = linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP4]]]
|
||||
// CHECK-SAME: ["parallel", "parallel", "parallel", "parallel"]
|
||||
// CHECK-SAME: ins(%[[ARG0]], %[[T0]] : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>)
|
||||
// CHECK: %[[T2:.+]] = linalg.tensor_reshape
|
||||
// CHECK-SAME: [#[[MAP0]], #[[MAP1]], #[[MAP2]]]
|
||||
// CHECK-SAME: tensor<?x?x?x?xf32> into tensor<?x?x?xf32>
|
||||
// CHECK: return %[[T2]]
|
||||
|
||||
// -----
|
||||
|
||||
#map0 = affine_map<(d0, d1) -> (d0, d1)>
|
||||
func @generic_op_reshape_consumer_fusion(%arg0 : tensor<?x?xf32>,
|
||||
%arg1 : tensor<?x?xf32>) ->
|
||||
tensor<?x?x4x5xf32>
|
||||
{
|
||||
%0 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel"]}
|
||||
ins(%arg0, %arg1 : tensor<?x?xf32>, tensor<?x?xf32>) {
|
||||
^bb0(%arg3: f32, %arg4: f32): // no predecessors
|
||||
%1 = mulf %arg3, %arg4 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?xf32>
|
||||
%1 = linalg.tensor_reshape %0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k, l)>] :
|
||||
tensor<?x?xf32> into tensor<?x?x4x5xf32>
|
||||
return %1 : tensor<?x?x4x5xf32>
|
||||
}
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2, d3) -> (d0)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2, d3) -> (d1, d2, d3)>
|
||||
// CHECK-DAG: #[[MAP2:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
// CHECK: func @generic_op_reshape_consumer_fusion
|
||||
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9_]+]]: tensor<?x?xf32>
|
||||
// CHECK-SAME: %[[ARG1:[a-zA-Z0-9_]+]]: tensor<?x?xf32>
|
||||
// CHECK: %[[T0:.+]] = linalg.tensor_reshape %[[ARG0]]
|
||||
// CHECK-SAME: [#[[MAP0]], #[[MAP1]]]
|
||||
// CHECK-SAME: tensor<?x?xf32> into tensor<?x?x4x5xf32>
|
||||
// CHECK: %[[T1:.+]] = linalg.tensor_reshape %[[ARG1]]
|
||||
// CHECK-SAME: [#[[MAP0]], #[[MAP1]]]
|
||||
// CHECK-SAME: tensor<?x?xf32> into tensor<?x?x4x5xf32>
|
||||
// CHECK: %[[T2:.+]] = linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[MAP2]], #[[MAP2]], #[[MAP2]]]
|
||||
// CHECK-SAME: ["parallel", "parallel", "parallel", "parallel"]
|
||||
// CHECK-SAME: ins(%[[T0]], %[[T1]] : tensor<?x?x4x5xf32>, tensor<?x?x4x5xf32>)
|
||||
// CHECK: return %[[T2]] : tensor<?x?x4x5xf32>
|
||||
|
||||
|
||||
// -----
|
||||
|
||||
func @reshape_as_consumer_permutation
|
||||
(%a : tensor<?x?x?xf32>, %b : tensor<?x?xf32>)
|
||||
-> tensor<?x?x?x?x?x?xf32> {
|
||||
%c = linalg.generic {
|
||||
indexing_maps = [affine_map<(d0, d1, d2) -> (d1, d0, d2)>,
|
||||
affine_map<(d0, d1, d2) -> (d1, d2)>,
|
||||
affine_map<(d0, d1, d2) -> (d0, d2, d1)>],
|
||||
iterator_types = ["parallel", "parallel", "parallel"]}
|
||||
ins(%a, %b : tensor<?x?x?xf32>, tensor<?x?xf32>) {
|
||||
^bb0(%arg0 : f32, %arg1: f32):
|
||||
%1 = addf %arg0, %arg1 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?x?xf32>
|
||||
%d = linalg.tensor_reshape %c
|
||||
[affine_map<(d0, d1, d2, d3, d4, d5) -> (d0, d1)>,
|
||||
affine_map<(d0, d1, d2, d3, d4, d5) -> (d2)>,
|
||||
affine_map<(d0, d1, d2, d3, d4, d5) -> (d3, d4, d5)>]
|
||||
: tensor<?x?x?xf32> into tensor<?x?x?x?x?x?xf32>
|
||||
return %d : tensor<?x?x?x?x?x?xf32>
|
||||
}
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2, d3, d4, d5) -> (d0, d1, d2)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2, d3, d4, d5) -> (d3, d4)>
|
||||
// CHECK-DAG: #[[MAP2:.+]] = affine_map<(d0, d1, d2, d3, d4, d5) -> (d5)>
|
||||
// CHECK-DAG: #[[MAP3:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)>
|
||||
// CHECK-DAG: #[[MAP4:.+]] = affine_map<(d0, d1, d2, d3) -> (d3)>
|
||||
// CHECK-DAG: #[[MAP5:.+]] = affine_map<(d0, d1, d2, d3, d4, d5) -> (d2, d3, d4, d0, d1, d5)>
|
||||
// CHECK-DAG: #[[MAP6:.+]] = affine_map<(d0, d1, d2, d3, d4, d5) -> (d2, d3, d4, d5)>
|
||||
// CHECK-DAG: #[[MAP7:.+]] = affine_map<(d0, d1, d2, d3, d4, d5) -> (d0, d1, d5, d2, d3, d4)>
|
||||
// CHECK: func @reshape_as_consumer_permutation
|
||||
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9_]+]]: tensor<?x?x?xf32>
|
||||
// CHECK-SAME: %[[ARG1:[a-zA-Z0-9_]+]]: tensor<?x?xf32>
|
||||
// CHECK: %[[T0:.+]] = linalg.tensor_reshape %[[ARG0]]
|
||||
// CHECK-SAME: [#[[MAP0]], #[[MAP1]], #[[MAP2]]]
|
||||
// CHECK-SAME: tensor<?x?x?xf32> into tensor<?x?x?x?x?x?xf32>
|
||||
// CHECK: %[[T1:.+]] = linalg.tensor_reshape %[[ARG1]]
|
||||
// CHECK-SAME: [#[[MAP3]], #[[MAP4]]]
|
||||
// CHECK-SAME: tensor<?x?xf32> into tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[T2:.+]] = linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[MAP5]], #[[MAP6]], #[[MAP7]]]
|
||||
// CHECK-SAME: ["parallel", "parallel", "parallel", "parallel", "parallel", "parallel"]
|
||||
// CHECK-SAME: ins(%[[T0]], %[[T1]] : tensor<?x?x?x?x?x?xf32>, tensor<?x?x?x?xf32>)
|
||||
// CHECK: return %[[T2]] : tensor<?x?x?x?x?x?xf32>
|
||||
|
||||
// -----
|
||||
|
||||
#map0 = affine_map<(d0, d1) -> (d0, d1)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d0, d1)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d2)>
|
||||
|
||||
func @generic_op_reshape_consumer_static(%arg0: tensor<264x4xf32>)
|
||||
-> tensor<8x33x4xf32> {
|
||||
%cst = constant dense<2.000000e+00> : tensor<264x4xf32>
|
||||
%0 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel"]}
|
||||
ins(%arg0, %cst : tensor<264x4xf32>, tensor<264x4xf32>) {
|
||||
^bb0(%arg1: f32, %arg2: f32): // no predecessors
|
||||
%2 = mulf %arg1, %arg2 : f32
|
||||
linalg.yield %2 : f32
|
||||
} -> tensor<264x4xf32>
|
||||
%1 = linalg.tensor_reshape %0 [#map1, #map2] :
|
||||
tensor<264x4xf32> into tensor<8x33x4xf32>
|
||||
return %1 : tensor<8x33x4xf32>
|
||||
}
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d0, d1)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d2)>
|
||||
// CHECK-DAG: #[[MAP2:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
// CHECK: func @generic_op_reshape_consumer_static
|
||||
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9_]+]]: tensor<264x4xf32>
|
||||
// CHECK: %[[T0:.+]] = linalg.tensor_reshape %[[ARG0]]
|
||||
// CHECK-SAME: [#[[MAP0]], #[[MAP1]]]
|
||||
// CHECK-SAME: tensor<264x4xf32> into tensor<8x33x4xf32>
|
||||
// CHECK: %[[T1:.+]] = linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[MAP2]], #[[MAP2]]]
|
||||
// CHECK-SAME: ["parallel", "parallel", "parallel"]
|
||||
// CHECK-SAME: ins(%[[T0]] : tensor<8x33x4xf32>)
|
||||
// CHECK: return %[[T1]] : tensor<8x33x4xf32>
|
||||
|
||||
// -----
|
||||
|
||||
func @scalar_reshape(%arg0 : tensor<1x10xf32>, %arg1 : tensor<1xf32>)
|
||||
-> tensor<1x10xf32> {
|
||||
%0 = linalg.tensor_reshape %arg1 [] : tensor<1xf32> into tensor<f32>
|
||||
%1 = linalg.generic
|
||||
{indexing_maps = [affine_map<(d0) -> ()>, affine_map<(d0) -> (d0)>],
|
||||
iterator_types = ["parallel"]} ins(%0 : tensor<f32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<10xf32>
|
||||
%2 = linalg.tensor_reshape %1 [affine_map<(d0, d1) -> (d0, d1)>]
|
||||
: tensor<10xf32> into tensor<1x10xf32>
|
||||
return %2 : tensor<1x10xf32>
|
||||
}
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1) -> ()>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1) -> (d0, d1)>
|
||||
// CHECK: func @scalar_reshape
|
||||
// CHECK-SAME: %[[ARG0:[a-zA-Z0-9_]+]]: tensor<1x10xf32>
|
||||
// CHECK-SAME: %[[ARG1:[a-zA-Z0-9_]+]]: tensor<1xf32>
|
||||
// CHECK: %[[T0:.+]] = linalg.tensor_reshape %[[ARG1]] []
|
||||
// CHECK-SAME: tensor<1xf32> into tensor<f32>
|
||||
// CHECK: %[[T1:.+]] = linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[MAP0]], #[[MAP1]]]
|
||||
// CHECK-SAME: iterator_types = ["parallel", "parallel"]
|
||||
// CHECK-SAME: ins(%[[T0]] : tensor<f32>)
|
||||
// CHECK: return %[[T1]] : tensor<1x10xf32>
|
||||
241
mlir/test/Dialect/Linalg/reshape_linearization_fusion.mlir
Normal file
241
mlir/test/Dialect/Linalg/reshape_linearization_fusion.mlir
Normal file
@@ -0,0 +1,241 @@
|
||||
// RUN: mlir-opt -split-input-file -linalg-fold-reshape-ops-by-linearization %s | FileCheck %s
|
||||
|
||||
|
||||
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1 * 4 + d2, d3)>
|
||||
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @generic_op_reshape_producer_fusion(%arg0 : tensor<?x?x?xf32>,
|
||||
%arg1 : tensor<?x?x4x?xf32>) ->
|
||||
tensor<?x?x4x?xf32>
|
||||
{
|
||||
%0 = linalg.tensor_reshape %arg0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k)>,
|
||||
affine_map<(i, j, k, l) -> (l)>] :
|
||||
tensor<?x?x?xf32> into tensor<?x?x4x?xf32>
|
||||
%1 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"]}
|
||||
ins(%0, %arg1 : tensor<?x?x4x?xf32>, tensor<?x?x4x?xf32>) {
|
||||
^bb0(%arg3: f32, %arg4: f32): // no predecessors
|
||||
%1 = mulf %arg3, %arg4 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?x4x?xf32>
|
||||
return %1 : tensor<?x?x4x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_reshape_producer_fusion
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.generic
|
||||
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1 * 20 + d2 * 5 + d3)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @generic_op_reshape_consumer_fusion(%arg0 : tensor<?x?x4x5xf32>,
|
||||
%arg1 : tensor<?x?x4x5xf32>) ->
|
||||
tensor<?x?xf32>
|
||||
{
|
||||
%0 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"]}
|
||||
ins(%arg0, %arg1 : tensor<?x?x4x5xf32>, tensor<?x?x4x5xf32>) {
|
||||
^bb0(%arg3: f32, %arg4: f32): // no predecessors
|
||||
%1 = mulf %arg3, %arg4 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?x4x5xf32>
|
||||
%1 = linalg.tensor_reshape %0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k, l)>] :
|
||||
tensor<?x?x4x5xf32> into tensor<?x?xf32>
|
||||
return %1 : tensor<?x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_reshape_consumer_fusion
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.generic
|
||||
|
||||
// -----
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @generic_op_reshape_consumer_nofusion(%arg0 : tensor<?x?x?x5xf32>,
|
||||
%arg1 : tensor<?x?x?x5xf32>) ->
|
||||
tensor<?x?xf32>
|
||||
{
|
||||
%0 = linalg.generic {
|
||||
indexing_maps = [#map0, #map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"]}
|
||||
ins(%arg0, %arg1 : tensor<?x?x?x5xf32>, tensor<?x?x?x5xf32>) {
|
||||
^bb0(%arg3: f32, %arg4: f32): // no predecessors
|
||||
%1 = mulf %arg3, %arg4 : f32
|
||||
linalg.yield %1 : f32
|
||||
} -> tensor<?x?x?x5xf32>
|
||||
%1 = linalg.tensor_reshape %0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k, l)>] :
|
||||
tensor<?x?x?x5xf32> into tensor<?x?xf32>
|
||||
return %1 : tensor<?x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_reshape_consumer_nofusion
|
||||
// CHECK: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
|
||||
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1 * 4 + d2, d3)>
|
||||
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @indexed_generic_op_reshape_producer_fusion(%arg0 : tensor<?x?x?xi32>)
|
||||
-> tensor<?x?x4x?xi32> {
|
||||
%0 = linalg.tensor_reshape %arg0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k)>,
|
||||
affine_map<(i, j, k, l) -> (l)>] :
|
||||
tensor<?x?x?xi32> into tensor<?x?x4x?xi32>
|
||||
%1 = linalg.indexed_generic {
|
||||
indexing_maps = [#map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"] }
|
||||
ins(%0 : tensor<?x?x4x?xi32>) {
|
||||
^bb0(%arg2: index, %arg3: index, %arg4: index, %arg5: index, %arg6: i32): // no predecessors
|
||||
%2 = index_cast %arg2 : index to i32
|
||||
%3 = addi %arg6, %2 : i32
|
||||
linalg.yield %3 : i32
|
||||
} -> tensor<?x?x4x?xi32>
|
||||
return %1 : tensor<?x?x4x?xi32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @indexed_generic_op_reshape_producer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.indexed_generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1, d2, d3) -> (d0, d1 * 20 + d2 * 5 + d3)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2, d3)>
|
||||
func @indexed_generic_op_reshape_consumer_fusion(%arg0 : tensor<?x?x4x5xi32>)
|
||||
-> tensor<?x?xi32> {
|
||||
%0 = linalg.indexed_generic {
|
||||
indexing_maps = [#map0, #map0],
|
||||
iterator_types = ["parallel", "parallel", "parallel", "parallel"] }
|
||||
ins(%arg0 : tensor<?x?x4x5xi32>) {
|
||||
^bb0(%arg2: index, %arg3: index, %arg4: index, %arg5: index, %arg6: i32): // no predecessors
|
||||
%2 = index_cast %arg2 : index to i32
|
||||
%3 = addi %arg6, %2 : i32
|
||||
linalg.yield %3 : i32
|
||||
} -> tensor<?x?x4x5xi32>
|
||||
%1 = linalg.tensor_reshape %0 [affine_map<(i, j, k, l) -> (i)>,
|
||||
affine_map<(i, j, k, l) -> (j, k, l)>] :
|
||||
tensor<?x?x4x5xi32> into tensor<?x?xi32>
|
||||
return %1 : tensor<?x?xi32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @indexed_generic_op_reshape_consumer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.indexed_generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d0, d1 + d2 * 7)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d0, d2, d1)>
|
||||
#map3 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
func @generic_op_021_permultation_reshape_producer_fusion(%arg0 : tensor<3x35xf32>) -> tensor<3x7x5xf32> {
|
||||
%0 = linalg.tensor_reshape %arg0 [#map0, #map1] : tensor<3x35xf32> into tensor<3x5x7xf32>
|
||||
%1 = linalg.generic {indexing_maps = [#map2, #map3], iterator_types = ["parallel", "parallel", "parallel"]} ins(%0 : tensor<3x5x7xf32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<3x7x5xf32>
|
||||
return %1 : tensor<3x7x5xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_021_permultation_reshape_producer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d2, d0 * 7 + d1)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d1, d2, d0)>
|
||||
#map3 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
func @generic_op_120_permultation_reshape_producer_fusion(%arg0 : tensor<3x35xf32>) -> tensor<5x7x3xf32> {
|
||||
%0 = linalg.tensor_reshape %arg0 [#map0, #map1] : tensor<3x35xf32> into tensor<3x5x7xf32>
|
||||
%1 = linalg.generic {indexing_maps = [#map2, #map3], iterator_types = ["parallel", "parallel", "parallel"]} ins(%0 : tensor<3x5x7xf32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<5x7x3xf32>
|
||||
return %1 : tensor<5x7x3xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_120_permultation_reshape_producer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d1, d0 * 7 + d2)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d1, d0, d2)>
|
||||
#map3 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
func @generic_op_102_permultation_reshape_producer_fusion(%arg0 : tensor<3x35xf32>) -> tensor<5x3x7xf32> {
|
||||
%0 = linalg.tensor_reshape %arg0 [#map0, #map1] : tensor<3x35xf32> into tensor<3x5x7xf32>
|
||||
%1 = linalg.generic {indexing_maps = [#map2, #map3], iterator_types = ["parallel", "parallel", "parallel"]} ins(%0 : tensor<3x5x7xf32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<5x3x7xf32>
|
||||
return %1 : tensor<5x3x7xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_102_permultation_reshape_producer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-DAG: #[[MAP0:.+]] = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
// CHECK-DAG: #[[MAP1:.+]] = affine_map<(d0, d1, d2) -> (d1, d0 * 7 + d2)>
|
||||
|
||||
|
||||
#map0 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
|
||||
#map1 = affine_map<(d0, d1, d2) -> (d1, d0, d2)>
|
||||
#map2 = affine_map<(d0, d1, d2) -> (d0)>
|
||||
#map3 = affine_map<(d0, d1, d2) -> (d1, d2)>
|
||||
func @generic_op_102_permultation_reshape_consumer_fusion(%arg0 : tensor<3x5x7xf32>) -> tensor<5x21xf32> {
|
||||
%0 = linalg.generic {indexing_maps = [#map0, #map1], iterator_types = ["parallel", "parallel", "parallel"]} ins(%arg0 : tensor<3x5x7xf32>) {
|
||||
^bb0(%arg2: f32): // no predecessors
|
||||
linalg.yield %arg2 : f32
|
||||
} -> tensor<5x3x7xf32>
|
||||
%1 = linalg.tensor_reshape %0 [#map2, #map3] : tensor<5x3x7xf32> into tensor<5x21xf32>
|
||||
return %1 : tensor<5x21xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @generic_op_102_permultation_reshape_consumer_fusion
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
// CHECK: linalg.generic
|
||||
// CHECK-SAME: indexing_maps = [#[[$MAP0]], #[[$MAP1]]]
|
||||
// CHECK-NOT: linalg.tensor_reshape
|
||||
Reference in New Issue
Block a user