@@ -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 <384 x512 xf32 >, %arg1: tensor <384 x512 xf32 >) -> tensor <?x512 x?x1 xf32 > {
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 <384 x512 xf32 >
1519+ %1 = linalg.generic {index ing_maps = [#map , #map , #map ], iterator_types = [" parallel" , " parallel" ]} ins (%arg0 , %arg1 : tensor <384 x512 xf32 >, tensor <384 x512 xf32 >) outs (%0 : tensor <384 x512 xf32 >) 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 <384 x512 xf32 >
1524+ %mouter = affine.apply affine_map <()[s0 ] -> (384 ceildiv s0 )>()[%c8_vscale ]
1525+ %2 = tensor.empty (%mouter , %c8_vscale ) : tensor <?x512 x?x1 xf32 >
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 <384 x512 xf32 > -> tensor <?x512 x?x1 xf32 >
1527+ return %pack : tensor <?x512 x?x1 xf32 >
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 <12 x?x7 x?xf32 >, %arg1: tensor <80 x320 xf32 >) -> tensor <80 x320 xf32 > {
1552+ %c8 = arith.constant 8 : index
1553+ %vscale = vector.vscale
1554+ %c8_vscale = arith.muli %vscale , %c8 : index
1555+ %0 = tensor.empty () : tensor <80 x320 xf32 >
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 <12 x?x7 x?xf32 > -> tensor <80 x320 xf32 >
1557+ %1 = linalg.generic {index ing_maps = [#map , #map , #map ], iterator_types = [" parallel" , " parallel" ]} ins (%arg1 , %unpack : tensor <80 x320 xf32 >, tensor <80 x320 xf32 >) outs (%0 : tensor <80 x320 xf32 >) 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 <80 x320 xf32 >
1562+ return %1 : tensor <80 x320 xf32 >
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 <384 x512 xf32 >, %arg1: tensor <384 x512 xf32 >, %d: index ) -> tensor <?x512 x?x1 xf32 > {
1586+ %cst = arith.constant 0.000000e+00 : f32
1587+ %0 = tensor.empty () : tensor <384 x512 xf32 >
1588+ %1 = linalg.generic {index ing_maps = [#map , #map , #map ], iterator_types = [" parallel" , " parallel" ]} ins (%arg0 , %arg1 : tensor <384 x512 xf32 >, tensor <384 x512 xf32 >) outs (%0 : tensor <384 x512 xf32 >) 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 <384 x512 xf32 >
1593+ %mouter = affine.apply affine_map <()[s0 ] -> (384 ceildiv s0 )>()[%d ]
1594+ %2 = tensor.empty (%mouter , %d ) : tensor <?x512 x?x1 xf32 >
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 <384 x512 xf32 > -> tensor <?x512 x?x1 xf32 >
1596+ return %pack : tensor <?x512 x?x1 xf32 >
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