The existing `scf::tileAndFuseConsumerOfSlices` takes a list of slices (and loops they are part of), tries to find the consumer of these slices (all slices are expected to be the same consumer), and then tiles the consumer into the loop nest using the `TilingInterface`. A more natural way of doing consumer fusion is to just start from the consumer, look for operands that are produced by the loop nest passed in as `loops` (presumably these loops are generated by tiling, but that is not a requirement for consumer fusion). Using the consumer you can find the slices of the operands that are accessed within the loop which you can then use to tile and fuse the consumer (using `TilingInterface`). This handles more naturally the case where multiple operands of the consumer come from the loop nest. The `scf::tileAndFuseConsumerOfSlices` was implemented as a mirror of `scf::tileAndFuseProducerOfSlice`. For the latter, the slice has a single producer for the source of the slice, which makes it a natural way of specifying producer fusion. But for consumers, the result might have multiple users, resulting in multiple candidates for fusion, as well as a fusion candidate using multiple results from the tiled loop nest. This means using slices (`tensor.insert_slice`/`tensor.parallel_insert_slice`) as a hook for consumer fusion turns out to be quite hard to navigate. The use of the consumer directly avoids all those pain points. In time the `scf::tileAndFuseConsumerOfSlices` should be deprecated in favor of `scf::tileAndFuseConsumer`. There is a lot of tech-debt that has accumulated in `scf::tileAndFuseConsumerOfSlices` that needs to be cleanedup. So while that gets cleaned up, and required functionality is moved to `scf::tileAndFuseConsumer`, the old path is still maintained. The test for `scf::tileAndFuseConsumerUsingSlices` is copied to `tile-and-fuse-consumer.mlir` to `tile-and-fuse-consumer-using-slices.mlir`. All the tests that were there in this file are now using the `tileAndFuseConsumer` method. The test op `test.tile_and_fuse_consumer` is modified to call `scf::tileAndFuseConsumer`, while a new op `test.tile_and_fuse_consumer_of_slice` is used to keep the old path tested while it is deprecated. --------- Signed-off-by: MaheshRavishankar <mahesh.ravishankar@gmail.com>
208 lines
8.5 KiB
TableGen
208 lines
8.5 KiB
TableGen
//===- TestTilingInterfaceTransformOps.td -----------------*- tablegen -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef TEST_TILINGINTERFACE_TRANSFORM_OPS
|
|
#define TEST_TILINGINTERFACE_TRANSFORM_OPS
|
|
|
|
include "mlir/Dialect/SCF/IR/DeviceMappingInterface.td"
|
|
include "mlir/Dialect/Transform/IR/TransformDialect.td"
|
|
include "mlir/Dialect/Transform/Interfaces/TransformInterfaces.td"
|
|
include "mlir/Dialect/Transform/IR/TransformTypes.td"
|
|
include "mlir/Interfaces/SideEffectInterfaces.td"
|
|
include "mlir/IR/OpBase.td"
|
|
|
|
// Those operations in this file are meant for testing the tiling interface
|
|
// transformations using scf operations. Over time these testing options
|
|
// might be useful transformations in their own right. Move these over
|
|
// as transform ops in the main repo (also find a proper place for them)
|
|
|
|
def TestFuseAndYieldOp : Op<Transform_Dialect, "test.fuse_and_yield",
|
|
[FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
|
|
DeclareOpInterfaceMethods<TransformOpInterface>,
|
|
ReportTrackingListenerFailuresOpTrait]> {
|
|
let description = [{
|
|
Tiles the operations pointed to by the target handle, fuses their
|
|
producers greedily using the options provided as attributes.
|
|
It also yields some of the fused producers for testing.
|
|
|
|
On success returns the tiled operations as well as generated loops. Emits
|
|
a definite failure if tiling fails.
|
|
}];
|
|
|
|
let arguments =
|
|
(ins TransformHandleTypeInterface:$target,
|
|
DefaultValuedAttr<I64ArrayAttr, "{}">:$tile_sizes,
|
|
DefaultValuedAttr<I64ArrayAttr, "{}">:$tile_interchange,
|
|
DefaultValuedAttr<BoolAttr, "false">:$use_forall);
|
|
let results = (outs TransformHandleTypeInterface:$transfomed,
|
|
Variadic<TransformHandleTypeInterface>:$loops);
|
|
|
|
let assemblyFormat = [{
|
|
$target ($tile_sizes^)? (`interchange` $tile_interchange^)?
|
|
(`use_forall` $use_forall^)? attr-dict
|
|
`:` functional-type(operands, results)
|
|
}];
|
|
}
|
|
|
|
def TestFuseConsumerUsingSliceOp : Op<Transform_Dialect, "test.fuse_consumer_using_slice",
|
|
[AttrSizedOperandSegments,
|
|
DeclareOpInterfaceMethods<TransformOpInterface>,
|
|
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
|
|
ReportTrackingListenerFailuresOpTrait]> {
|
|
let description = [{
|
|
For the `insert_slice`-like operations (that are typically generated through tiling),
|
|
within the loop nests passed in as `loops` (that are typically generated through tiling),
|
|
find the consumer that these slices map to (have to be the same consumer) and fuse
|
|
the consumer into the loop.
|
|
|
|
Returns a handle to the original consumer operation and the consumer operation after
|
|
fusion.
|
|
}];
|
|
|
|
let arguments = (ins
|
|
Variadic<TransformHandleTypeInterface>:$targets,
|
|
Variadic<TransformHandleTypeInterface>:$loops,
|
|
DefaultValuedAttr<I32Attr, "1">:$num_consumer_to_fuse);
|
|
let results = (outs TransformHandleTypeInterface:$consumer,
|
|
TransformHandleTypeInterface:$fused_consumer);
|
|
|
|
let assemblyFormat = [{
|
|
$targets `in` `(` $loops `)`
|
|
(`num_consumer_to_fuse` `=` $num_consumer_to_fuse^)?
|
|
attr-dict `:` functional-type(operands, results)
|
|
}];
|
|
}
|
|
|
|
def TestFuseConsumerOp : Op<Transform_Dialect, "test.fuse_consumer",
|
|
[DeclareOpInterfaceMethods<TransformOpInterface>,
|
|
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
|
|
ReportTrackingListenerFailuresOpTrait]> {
|
|
let description = [{
|
|
For the `consumer` that uses the result of the outer-most loop of a loop nest passed in
|
|
as `loops` (that are typically generated through tiling), fuse the consumer into the
|
|
loop.
|
|
|
|
Returns a handle to the consumer operation after fusion and the loops that might be
|
|
modified.
|
|
}];
|
|
|
|
let arguments = (ins
|
|
TransformHandleTypeInterface:$consumer,
|
|
Variadic<TransformHandleTypeInterface>:$loops);
|
|
let results = (outs TransformHandleTypeInterface:$fused_consumer,
|
|
Variadic<TransformHandleTypeInterface>:$result_loops);
|
|
|
|
let assemblyFormat = [{
|
|
$consumer `into` `(` $loops `)`
|
|
attr-dict `:` functional-type(operands, results)
|
|
}];
|
|
}
|
|
|
|
|
|
def TestTileUsingForallOp : Op<Transform_Dialect, "test.tile_using_forall",
|
|
[DeclareOpInterfaceMethods<TransformOpInterface>,
|
|
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
|
|
ReportTrackingListenerFailuresOpTrait]> {
|
|
let description = [{
|
|
Test operation use to test tiling using TilingInterface and scf.forall for
|
|
the loop constructs. This is similar to
|
|
`transform.structured.tile_using_for`. Use of this operation is an
|
|
intermediate state and will be replaced in due course with either
|
|
`transform.structured.tile_using_for` or
|
|
`transform.structured.tile_using_forall`.
|
|
|
|
On success returns the tiled operations as well as generated loops. Emits
|
|
a definite failure if tiling fails.
|
|
}];
|
|
|
|
let arguments = (ins TransformHandleTypeInterface:$target,
|
|
DefaultValuedAttr<I64ArrayAttr, "{}">:$tile_sizes,
|
|
DefaultValuedOptionalAttr<I64ArrayAttr, "{}">:$interchange,
|
|
OptionalAttr<DeviceMappingArrayAttr>:$mapping);
|
|
let results = (outs TransformHandleTypeInterface:$tiled_op,
|
|
Variadic<TransformHandleTypeInterface>:$loops);
|
|
|
|
let assemblyFormat = [{
|
|
$target ($tile_sizes^)? (`interchange` `=` $interchange^)?
|
|
(`mapping` `=` $mapping^)?
|
|
attr-dict `:` functional-type(operands, results)
|
|
}];
|
|
}
|
|
|
|
def TestFuseUsingForallOp : Op<Transform_Dialect, "test.fuse_using_forall",
|
|
[DeclareOpInterfaceMethods<TransformOpInterface>,
|
|
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
|
|
ReportTrackingListenerFailuresOpTrait]> {
|
|
let description = [{
|
|
Test operation to tile the operation pointed to by the target handle and
|
|
fuses their producers greedily using the options provided as attributes.
|
|
This operation uses scf.forall for the loop construct.
|
|
}];
|
|
let arguments = (ins TransformHandleTypeInterface:$root_op,
|
|
DefaultValuedAttr<I64ArrayAttr, "{}">:$tile_sizes,
|
|
DefaultValuedOptionalAttr<I64ArrayAttr, "{}">:$interchange,
|
|
OptionalAttr<DeviceMappingArrayAttr>:$mapping);
|
|
let results = (outs TransformHandleTypeInterface:$tiled_ops,
|
|
Variadic<TransformHandleTypeInterface>:$loops);
|
|
|
|
let assemblyFormat = [{
|
|
$root_op ($tile_sizes^)? (`interchange` $interchange^)?
|
|
(`mapping` `=` $mapping^)?
|
|
attr-dict `:` functional-type(operands, results)
|
|
}];
|
|
}
|
|
|
|
def TestTileAndFuseOuterParallelPartialReductionOp : Op<
|
|
Transform_Dialect, "test.tile_and_fuse_outer_parallel_partial_reduction",
|
|
[FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
|
|
DeclareOpInterfaceMethods<TransformOpInterface>,
|
|
ReportTrackingListenerFailuresOpTrait]> {
|
|
let description = [{
|
|
Test operation to tile an operation using partial reduction with
|
|
outer parallel strategy, and to fuse its producers.
|
|
}];
|
|
|
|
let arguments = (ins TransformHandleTypeInterface:$root_op,
|
|
DefaultValuedAttr<I64ArrayAttr, "{}">:$reduction_dims,
|
|
DefaultValuedAttr<I64ArrayAttr, "{}">:$tile_sizes,
|
|
OptionalAttr<DeviceMappingArrayAttr>:$mapping);
|
|
|
|
let results = (outs TransformHandleTypeInterface:$tiled_ops,
|
|
Variadic<TransformHandleTypeInterface>:$loops);
|
|
let assemblyFormat = [{
|
|
$root_op (`reduction_dims` `=` $reduction_dims^)?
|
|
(`tile_sizes` `=` $tile_sizes^)? (`mapping` `=` $mapping^)?
|
|
attr-dict `:` functional-type(operands, results)
|
|
}];
|
|
}
|
|
|
|
def TestTileUsingCustomLoopOp : Op<
|
|
Transform_Dialect, "test.tile_using_custom_loop",
|
|
[FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
|
|
DeclareOpInterfaceMethods<TransformOpInterface>,
|
|
ReportTrackingListenerFailuresOpTrait]> {
|
|
let description = [{
|
|
Test Transform op to tile an operation using custom loops.
|
|
|
|
The test just folds all the loops and into a single loop and then
|
|
delinearizes the indices.
|
|
}];
|
|
|
|
let arguments = (ins TransformHandleTypeInterface:$root_op,
|
|
DefaultValuedAttr<I64ArrayAttr, "{}">:$tile_sizes);
|
|
let results = (outs TransformHandleTypeInterface:$tiled_ops,
|
|
Variadic<TransformHandleTypeInterface>:$loops);
|
|
|
|
let assemblyFormat = [{
|
|
$root_op `tile_sizes` `=` $tile_sizes
|
|
attr-dict `:` functional-type(operands, results)
|
|
}];
|
|
}
|
|
|
|
#endif // TEST_TILINGINTERFACE_TRANSFORM_OPS
|