Skip to content

Commit c9e28a1

Browse files
egebeyselclaude
andcommitted
[Codegen] Forward distribution inner-tile alignment hints in forall tiling
Threads the distribution-level inner-tile alignment hints (set on scalable `linalg.pack`/`linalg.unpack` ops during lowering-strategy selection) into `TileAndDistributeToWorkgroupsUsingForall`, via `makeInnerTileAlignmentFn(DistributionTiles)` on both the tiling options and the consumer-fusion step. With a `Multiple` hint the workgroup tiling folds a scalable/dynamic inner tile to a clean `tile ceildiv innerTile` division instead of leaving the pack/unpack untiled. Adds lit coverage: two scalable pack/unpack cases (hint forwarded and preserved through tiling) and a dynamic-inner-tile case where the hint is load-bearing — the pack is only tiled into the forall because the hint asserts the multiple relationship the driver cannot infer from the IR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Ege Beysel <beyselege@gmail.com>
1 parent b57810e commit c9e28a1

2 files changed

Lines changed: 121 additions & 2 deletions

File tree

compiler/src/iree/compiler/Codegen/Common/TileDispatchUsingForall.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,10 +267,18 @@ void TileAndDistributeToWorkgroupsUsingForallOpPass::runOnOperation() {
267267
context, funcOp.getLoc(), deviceMappingAttribute))) {
268268
return signalPassFailure();
269269
}
270+
// Forward the distribution-level inner-tile alignment hints recorded on
271+
// tiled/fused `linalg.pack`/`linalg.unpack` ops, so the workgroup tiling can
272+
// fold their scalable inner tiles to a static shape where a hint guarantees
273+
// the distribution tile is a whole multiple of the inner tile.
274+
scf::InnerTileAlignmentFnTy innerTileAlignmentFn =
275+
makeInnerTileAlignmentFn(IREE::CPU::TilingLevel::DistributionTiles);
276+
270277
scf::SCFTilingOptions tilingOptions;
271278
tilingOptions.setTileSizes(tilingInfo->tileSizes);
272279
tilingOptions.setInterchange(tilingInfo->interchange);
273280
tilingOptions.setMapping(deviceMappingAttribute);
281+
tilingOptions.setInnerTileAlignmentFn(innerTileAlignmentFn);
274282

275283
IREE::Codegen::WorkgroupReorderingAttrInterface workgroupReorderingStrategy =
276284
getLoweringConfig(tilingInfo->tilableOp).getWorkgroupReorderingStrategy();
@@ -370,9 +378,11 @@ void TileAndDistributeToWorkgroupsUsingForallOpPass::runOnOperation() {
370378
FailureOr<std::queue<Operation *>> newFusionOpportunities =
371379
fuseConsumersIntoForall(
372380
rewriter, tileAndFuseResult->tiledAndFusedOps.getArrayRef(),
373-
tilingLoops, [&tiledAndFusedOps](Operation *op) {
381+
tilingLoops,
382+
[&tiledAndFusedOps](Operation *op) {
374383
return tiledAndFusedOps.contains(op);
375-
});
384+
},
385+
innerTileAlignmentFn);
376386
if (failed(newFusionOpportunities)) {
377387
// Continue the work if the failure is allowed.
378388
if (!verifyComputeOpsAfterDistribution(funcOp)) {

compiler/src/iree/compiler/Codegen/Common/test/tile_and_distribute_workgroups_using_forall.mlir

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,3 +1498,112 @@ func.func @arg_compare_fold_broadcast(
14981498
// CHECK-NOT: linalg.broadcast
14991499
// CHECK: scf.forall {{.*}} shared_outs(%{{.*}} = %[[INIT_F16]], %{{.*}} = %[[INIT_I32]])
15001500
// CHECK: iree_linalg_ext.arg_compare
1501+
1502+
// -----
1503+
1504+
// Distribution of a scalable linalg.pack (inner tiles [8*vscale, 1]) with an
1505+
// elementwise-add producer fused into the workgroup forall. The pack carries a
1506+
// `distribution = [Multiple, Unknown]` inner-tile alignment hint recorded during
1507+
// lowering-strategy selection, asserting that the distribution tile (64) on the
1508+
// packed dim is a whole multiple of the scalable inner tile. The forall tiling
1509+
// forwards that hint, so the fused pack tiles cleanly: its outer packed dim is
1510+
// `64 ceildiv (8 * vscale)`.
1511+
#config = #iree_codegen.lowering_config<tile_sizes = [[64, 64]]>
1512+
#map = affine_map<(d0, d1) -> (d0, d1)>
1513+
func.func @scalable_pack_distribute_with_producer(%arg0: tensor<384x512xf32>, %arg1: tensor<384x512xf32>) -> tensor<?x512x?x1xf32> {
1514+
%cst = arith.constant 0.000000e+00 : f32
1515+
%c8 = arith.constant 8 : index
1516+
%vscale = vector.vscale
1517+
%c8_vscale = arith.muli %vscale, %c8 : index
1518+
%0 = tensor.empty() : tensor<384x512xf32>
1519+
%1 = linalg.generic {indexing_maps = [#map, #map, #map], iterator_types = ["parallel", "parallel"]} ins(%arg0, %arg1 : tensor<384x512xf32>, tensor<384x512xf32>) outs(%0 : tensor<384x512xf32>) attrs = {lowering_config = #config} {
1520+
^bb0(%in: f32, %in_0: f32, %out: f32):
1521+
%3 = arith.addf %in, %in_0 : f32
1522+
linalg.yield %3 : f32
1523+
} -> tensor<384x512xf32>
1524+
%mouter = affine.apply affine_map<()[s0] -> (384 ceildiv s0)>()[%c8_vscale]
1525+
%2 = tensor.empty(%mouter, %c8_vscale) : tensor<?x512x?x1xf32>
1526+
%pack = linalg.pack %1 padding_value(%cst : f32) outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [%c8_vscale, 1] into %2 {inner_tile_alignments = #iree_cpu.inner_tile_alignments<distribution = [Multiple, Unknown]>} : tensor<384x512xf32> -> tensor<?x512x?x1xf32>
1527+
return %pack : tensor<?x512x?x1xf32>
1528+
}
1529+
// CHECK-LABEL: func.func @scalable_pack_distribute_with_producer
1530+
// CHECK: %[[VS:.+]] = vector.vscale
1531+
// CHECK: %[[C8VS:.+]] = arith.muli %[[VS]], %{{.+}}
1532+
// CHECK: scf.forall {{.*}} = (0, 0) to (384, 512) step (64, 64)
1533+
// CHECK: linalg.generic
1534+
// CHECK: arith.addf
1535+
// CHECK: %[[OUTER:.+]] = affine.apply affine_map<()[s0] -> (64 ceildiv s0)>()[%[[C8VS]]]
1536+
// CHECK: tensor.extract_slice %{{.+}}[%{{.+}}, %{{.+}}, 0, 0] [%[[OUTER]], 64, %[[C8VS]], 1]
1537+
// CHECK: linalg.pack
1538+
// CHECK-SAME: inner_tiles = [%[[C8VS]], 1]
1539+
// CHECK-SAME: inner_tile_alignments = #iree_cpu.inner_tile_alignments<distribution = [Multiple, Unknown]>
1540+
// CHECK: mapping = [#iree_codegen.workgroup_mapping<y>, #iree_codegen.workgroup_mapping<x>]
1541+
1542+
// -----
1543+
1544+
// Distribution of a scalable linalg.unpack (inner tiles [7, 8*vscale]) fused as
1545+
// the producer of an elementwise-add consumer. The unpack's
1546+
// `distribution = [Unknown, Multiple]` hint asserts that the distribution tile
1547+
// (64) on the unpacked N dim is a whole multiple of the scalable inner tile, so
1548+
// the fused unpack slices its packed source with `64 ceildiv (8 * vscale)`.
1549+
#config = #iree_codegen.lowering_config<tile_sizes = [[84, 64]]>
1550+
#map = affine_map<(d0, d1) -> (d0, d1)>
1551+
func.func @scalable_unpack_distribute_with_consumer(%arg0: tensor<12x?x7x?xf32>, %arg1: tensor<80x320xf32>) -> tensor<80x320xf32> {
1552+
%c8 = arith.constant 8 : index
1553+
%vscale = vector.vscale
1554+
%c8_vscale = arith.muli %vscale, %c8 : index
1555+
%0 = tensor.empty() : tensor<80x320xf32>
1556+
%unpack = linalg.unpack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [7, %c8_vscale] into %0 {inner_tile_alignments = #iree_cpu.inner_tile_alignments<distribution = [Unknown, Multiple]>} : tensor<12x?x7x?xf32> -> tensor<80x320xf32>
1557+
%1 = linalg.generic {indexing_maps = [#map, #map, #map], iterator_types = ["parallel", "parallel"]} ins(%arg1, %unpack : tensor<80x320xf32>, tensor<80x320xf32>) outs(%0 : tensor<80x320xf32>) attrs = {lowering_config = #config} {
1558+
^bb0(%in: f32, %in_0: f32, %out: f32):
1559+
%2 = arith.addf %in, %in_0 : f32
1560+
linalg.yield %2 : f32
1561+
} -> tensor<80x320xf32>
1562+
return %1 : tensor<80x320xf32>
1563+
}
1564+
// CHECK-LABEL: func.func @scalable_unpack_distribute_with_consumer
1565+
// CHECK: %[[VS:.+]] = vector.vscale
1566+
// CHECK: %[[C8VS:.+]] = arith.muli %[[VS]], %{{.+}}
1567+
// CHECK: scf.forall {{.*}} = (0, 0) to (80, 320) step (84, 64)
1568+
// CHECK: %[[NOUTER:.+]] = affine.apply affine_map<()[s0] -> (64 ceildiv s0)>()[%[[C8VS]]]
1569+
// CHECK: tensor.extract_slice %{{.+}}[0, %{{.+}}, 0, 0] [12, %[[NOUTER]], 7, %[[C8VS]]]
1570+
// CHECK: linalg.unpack
1571+
// CHECK-SAME: inner_tiles = [7, %[[C8VS]]]
1572+
// CHECK-SAME: inner_tile_alignments = #iree_cpu.inner_tile_alignments<distribution = [Unknown, Multiple]>
1573+
// CHECK: linalg.generic
1574+
// CHECK: arith.addf
1575+
// CHECK: mapping = [#iree_codegen.workgroup_mapping<y>, #iree_codegen.workgroup_mapping<x>]
1576+
1577+
// -----
1578+
1579+
// A dynamic (non-vscale) inner tile: the tiling driver cannot infer the multiple
1580+
// relationship from the IR, so the `distribution = [Multiple, Unknown]` hint is
1581+
// what lets the pack be tiled and fused into the workgroup forall (its outer
1582+
// packed dim becomes `64 ceildiv %d`). Without the hint the pack stays untiled.
1583+
#config = #iree_codegen.lowering_config<tile_sizes = [[64, 64]]>
1584+
#map = affine_map<(d0, d1) -> (d0, d1)>
1585+
func.func @dyn_pack_distribute_multiple_hint(%arg0: tensor<384x512xf32>, %arg1: tensor<384x512xf32>, %d: index) -> tensor<?x512x?x1xf32> {
1586+
%cst = arith.constant 0.000000e+00 : f32
1587+
%0 = tensor.empty() : tensor<384x512xf32>
1588+
%1 = linalg.generic {indexing_maps = [#map, #map, #map], iterator_types = ["parallel", "parallel"]} ins(%arg0, %arg1 : tensor<384x512xf32>, tensor<384x512xf32>) outs(%0 : tensor<384x512xf32>) attrs = {lowering_config = #config} {
1589+
^bb0(%in: f32, %in_0: f32, %out: f32):
1590+
%3 = arith.addf %in, %in_0 : f32
1591+
linalg.yield %3 : f32
1592+
} -> tensor<384x512xf32>
1593+
%mouter = affine.apply affine_map<()[s0] -> (384 ceildiv s0)>()[%d]
1594+
%2 = tensor.empty(%mouter, %d) : tensor<?x512x?x1xf32>
1595+
%pack = linalg.pack %1 padding_value(%cst : f32) outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [%d, 1] into %2 {inner_tile_alignments = #iree_cpu.inner_tile_alignments<distribution = [Multiple, Unknown]>} : tensor<384x512xf32> -> tensor<?x512x?x1xf32>
1596+
return %pack : tensor<?x512x?x1xf32>
1597+
}
1598+
// CHECK-LABEL: func.func @dyn_pack_distribute_multiple_hint
1599+
// CHECK-SAME: %[[D:[A-Za-z0-9_]+]]: index
1600+
// CHECK: scf.forall {{.*}} = (0, 0) to (384, 512) step (64, 64)
1601+
// CHECK: linalg.generic
1602+
// CHECK: arith.addf
1603+
// CHECK: %[[OUTER:.+]] = affine.apply affine_map<()[s0] -> (64 ceildiv s0)>()[%[[D]]]
1604+
// CHECK: tensor.extract_slice %{{.+}}[%{{.+}}, %{{.+}}, 0, 0] [%[[OUTER]], 64, %[[D]], 1]
1605+
// CHECK: linalg.pack
1606+
// CHECK-SAME: inner_tiles = [%[[D]], 1]
1607+
// CHECK-SAME: inner_tile_alignments = #iree_cpu.inner_tile_alignments<distribution = [Multiple, Unknown]>
1608+
// CHECK: scf.forall.in_parallel
1609+
// CHECK: mapping = [#iree_codegen.workgroup_mapping<y>, #iree_codegen.workgroup_mapping<x>]

0 commit comments

Comments
 (0)