| // RUN: mlir-opt --transform-interpreter --mlir-print-local-scope --split-input-file --verify-diagnostics --cse %s | FileCheck %s |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [4, 4, 4] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // CHECK-LABEL: func @tile_linalg_matmul( |
| // CHECK-SAME: %[[TA:[0-9a-z]+]]: tensor<128x128xf32> |
| // CHECK-SAME: %[[TB:[0-9a-z]+]]: tensor<128x128xf32> |
| // CHECK-SAME: %[[TC:[0-9a-z]+]]: tensor<128x128xf32> |
| // CHECK-SAME: -> tensor<128x128xf32> { |
| func.func @tile_linalg_matmul( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| // CHECK: %[[TD0:.*]] = scf.for {{.*}} to {{.*}} step {{.*}} iter_args(%[[TC0:.*]] = %[[TC]]) -> (tensor<128x128xf32>) { |
| // CHECK: %[[TD1:.*]] = scf.for {{.*}} to {{.*}} step {{.*}} iter_args(%[[TC1:.*]] = %[[TC0]]) -> (tensor<128x128xf32>) { |
| // CHECK: %[[TD2:.*]] = scf.for {{.*}} to {{.*}} step {{.*}} iter_args(%[[TC2:.*]] = %[[TC1]]) -> (tensor<128x128xf32>) { |
| // CHECK: %[[sTA:.*]] = tensor.extract_slice %[[TA]][{{.*}}] : tensor<128x128xf32> to tensor<4x4xf32> |
| // CHECK: %[[sTB:.*]] = tensor.extract_slice %[[TB]][{{.*}}] : tensor<128x128xf32> to tensor<4x4xf32> |
| // CHECK: %[[sTC:.*]] = tensor.extract_slice %[[TC2]][{{.*}}] : tensor<128x128xf32> to tensor<4x4xf32> |
| // CHECK: %[[sTD:.*]] = linalg.matmul ins(%[[sTA]], %[[sTB]] : tensor<4x4xf32>, tensor<4x4xf32>) |
| // CHECK-SAME: outs(%[[sTC]] : tensor<4x4xf32>) -> tensor<4x4xf32> |
| // CHECK: %[[TD:.*]] = tensor.insert_slice %[[sTD]] into %[[TC2]][{{.*}}] : tensor<4x4xf32> into tensor<128x128xf32> |
| // CHECK: scf.yield %[[TD]] : tensor<128x128xf32> |
| // CHECK: scf.yield %[[TD2]] : tensor<128x128xf32> |
| // CHECK: scf.yield %[[TD1]] : tensor<128x128xf32> |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| |
| // CHECK: return %[[TD0]] : tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1 = transform.structured.match ops{["func.call"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %2, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [%1, %1, 4] : (!transform.any_op, !transform.any_op, !transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func private @get_dynamic_tile_size() -> index |
| |
| // CHECK-LABEL: func @tile_linalg_matmul_dynamic( |
| // CHECK-SAME: %[[TA:[0-9a-z]+]]: tensor<128x128xf32> |
| // CHECK-SAME: %[[TB:[0-9a-z]+]]: tensor<128x128xf32> |
| // CHECK-SAME: %[[TC:[0-9a-z]+]]: tensor<128x128xf32> |
| // CHECK-SAME: -> tensor<128x128xf32> { |
| func.func @tile_linalg_matmul_dynamic( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| // CHECK: %[[TD0:.*]] = scf.for {{.*}} to {{.*}} step {{.*}} iter_args(%[[TC0:.*]] = %[[TC]]) -> (tensor<128x128xf32>) { |
| // CHECK: %[[TD1:.*]] = scf.for {{.*}} to {{.*}} step {{.*}} iter_args(%[[TC1:.*]] = %[[TC0]]) -> (tensor<128x128xf32>) { |
| // CHECK: %[[TD2:.*]] = scf.for {{.*}} to {{.*}} step {{.*}} iter_args(%[[TC2:.*]] = %[[TC1]]) -> (tensor<128x128xf32>) { |
| // CHECK: %[[sTA:.*]] = tensor.extract_slice %[[TA]][{{.*}}] : tensor<128x128xf32> to tensor<?x4xf32> |
| // CHECK: %[[sTB:.*]] = tensor.extract_slice %[[TB]][{{.*}}] : tensor<128x128xf32> to tensor<4x?xf32> |
| // CHECK: %[[sTC:.*]] = tensor.extract_slice %[[TC2]][{{.*}}] : tensor<128x128xf32> to tensor<?x?xf32> |
| // CHECK: %[[sTD:.*]] = linalg.matmul ins(%[[sTA]], %[[sTB]] : tensor<?x4xf32>, tensor<4x?xf32>) |
| // CHECK-SAME: outs(%[[sTC]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| // CHECK: %[[TD:.*]] = tensor.insert_slice %[[sTD]] into %[[TC2]][{{.*}}] : tensor<?x?xf32> into tensor<128x128xf32> |
| // CHECK: scf.yield %[[TD]] : tensor<128x128xf32> |
| // CHECK: scf.yield %[[TD2]] : tensor<128x128xf32> |
| // CHECK: scf.yield %[[TD1]] : tensor<128x128xf32> |
| %sz = func.call @get_dynamic_tile_size() : () -> index |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| |
| // CHECK: return %[[TD0]] : tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %c8 = transform.param.constant 8 : i64 -> !transform.any_param |
| %c16 = transform.param.constant 16 : i64 -> !transform.any_param |
| %tiles = transform.merge_handles %c8, %c16 : !transform.any_param |
| %1, %loops = transform.structured.tile_using_for %0 tile_sizes *(%tiles) |
| : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op) |
| // Verify that correct number of loops is present in packed result. |
| %loop:2 = transform.split_handle %loops : (!transform.any_op) |
| -> (!transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // CHECK-LABEL: func @tile_linalg_matmul_packed_tile_sizes( |
| // CHECK: %[[SZ0:.*]] = arith.constant 8 : index |
| // CHECK: %[[SZ1:.*]] = arith.constant 16 : index |
| // CHECK: %[[TD0:.*]] = scf.for {{.*}} step %[[SZ0]] iter_args |
| // CHECK: %[[TD1:.*]] = scf.for {{.*}} step %[[SZ1]] iter_args |
| // CHECK-NOT: scf.for |
| // CHECK: return %[[TD0]] : tensor<128x128xf32> |
| func.func @tile_linalg_matmul_packed_tile_sizes( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %c8 = transform.param.constant 8 : i64 -> !transform.any_param |
| %c16 = transform.param.constant 16 : i64 -> !transform.any_param |
| %tiles = transform.merge_handles %c8, %c16 : !transform.any_param |
| // expected-error @below {{op expected number of loops to tile (1) to match number of `loops` results (2)}} |
| %1, %loops:2 = transform.structured.tile_using_for %0 tile_sizes *(%tiles) |
| : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_packed_tile_sizes_loop_result_arity_mismatch( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %c8 = transform.param.constant 8 : i64 -> !transform.any_param |
| %c16 = transform.param.constant 16 : i64 -> !transform.any_param |
| %tiles = transform.merge_handles %c8, %c16 : !transform.any_param |
| %1, %loops = transform.structured.tile_using_for %0 tile_sizes *(%tiles) |
| : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op) |
| // Verify that correct number of loops is present in packed result. |
| %loop:4 = transform.split_handle %loops : (!transform.any_op) |
| -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @tile_linalg_matmul_packed_tile_sizes_multiple_targets( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> (tensor<128x128xf32>, tensor<128x128xf32>) { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| %1 = linalg.matmul ins(%0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0, %1 : tensor<128x128xf32>, tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %c1 = transform.param.constant 1 : i64 -> !transform.any_param |
| %c0 = transform.param.constant 0 : i64 -> !transform.any_param |
| %c2 = transform.param.constant 2 : i64 -> !transform.any_param |
| %interchange = transform.merge_handles %c1, %c0, %c2 : !transform.any_param |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [2, 4, 8] interchange = *(%interchange) |
| : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // CHECK-LABEL: func @tile_linalg_matmul_packed_interchange( |
| // CHECK-DAG: %[[C2:.*]] = arith.constant 2 : index |
| // CHECK-DAG: %[[C4:.*]] = arith.constant 4 : index |
| // CHECK-DAG: %[[C8:.*]] = arith.constant 8 : index |
| // CHECK: %[[L0:.*]] = scf.for {{.*}} step %[[C4]] iter_args |
| // CHECK: %[[L1:.*]] = scf.for {{.*}} step %[[C2]] iter_args |
| // CHECK: %[[L2:.*]] = scf.for {{.*}} step %[[C8]] iter_args |
| func.func @tile_linalg_matmul_packed_interchange( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %c8 = transform.param.constant 8 : i64 -> !transform.any_param |
| %c16 = transform.param.constant 16 : i64 -> !transform.any_param |
| %tiles = transform.merge_handles %c8, %c16 : !transform.any_param |
| %c1 = transform.param.constant 1 : i64 -> !transform.any_param |
| %c0 = transform.param.constant 0 : i64 -> !transform.any_param |
| %interchange = transform.merge_handles %c1, %c0 : !transform.any_param |
| %1, %loops = transform.structured.tile_using_for %0 tile_sizes *(%tiles) interchange = *(%interchange) |
| : (!transform.any_op, !transform.any_param, !transform.any_param) -> (!transform.any_op, !transform.any_op) |
| // Verify that correct number of loops is present in packed result. |
| %loop:2 = transform.split_handle %loops : (!transform.any_op) |
| -> (!transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @tile_linalg_matmul_packed_tile_sizes_and_packed_interchange( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-note @below {{for this parameter}} |
| %c0 = transform.test_produce_param (0 : i64) : !transform.param<i64> |
| %c0_as_any = transform.test_produce_param (0 : i64) : !transform.any_param |
| // expected-error @below {{expected as many parameter values (0) as target ops (2)}} |
| transform.structured.tile_using_for %0 tile_sizes [%c0, %c0, %c0_as_any] |
| : (!transform.any_op, !transform.param<i64>, !transform.param<i64>, !transform.any_param) |
| -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_param_value_count_mismatch( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> (tensor<128x128xf32>, tensor<128x128xf32>) { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| %1 = linalg.matmul ins(%0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0, %1 : tensor<128x128xf32>, tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-note @below {{for this handle}} |
| %1 = transform.structured.match ops{["arith.constant"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-error @below {{expected as many dynamic size-producing operations (0) as target ops (2)}} |
| transform.structured.tile_using_for %0 tile_sizes [%1, %1, 1] |
| : (!transform.any_op, !transform.any_op, !transform.any_op) |
| -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_size_producer_count_mismatch( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> (tensor<128x128xf32>, tensor<128x128xf32>) { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| %1 = linalg.matmul ins(%0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0, %1 : tensor<128x128xf32>, tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| // CHECK-LABEL: tile_tensor_pad |
| func.func @tile_tensor_pad( |
| %arg0 : tensor<?x?xf32>, %cst : f32, %low: index, %high: index) |
| -> tensor<20x40xf32> |
| { |
| // CHECK: scf.forall |
| // CHECK: scf.if |
| // CHECK: tensor.generate |
| // CHECK: else |
| // CHECK: tensor.pad {{.*}} nofold |
| %0 = tensor.pad %arg0 nofold low[%low, %low] high[%high, %high] { |
| ^bb0(%arg9: index, %arg10: index): |
| tensor.yield %cst : f32 |
| } : tensor<?x?xf32> to tensor<20x40xf32> |
| return %0 : tensor<20x40xf32> |
| } |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["tensor.pad"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| transform.structured.tile_using_forall %0 tile_sizes[1, 1] |
| : (!transform.any_op) -> (!transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // ----- |
| |
| #map = affine_map<(d0) -> (d0)> |
| |
| module { |
| func.func @scalable_tile(%arg0: tensor<?xf32>, %arg1: tensor<?xf32>, %arg2: tensor<?xf32>, %arg3: f32) -> tensor<?xf32> { |
| %0 = linalg.generic {indexing_maps = [#map, #map, #map], iterator_types = ["parallel"]} ins(%arg0, %arg1 : tensor<?xf32>, tensor<?xf32>) outs(%arg2 : tensor<?xf32>) { |
| ^bb0(%in_1: f32, %in_2: f32, %out: f32): |
| %1 = arith.addf %in_1, %in_2 : f32 |
| %2 = arith.mulf %arg3, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?xf32> |
| return %0 : tensor<?xf32> |
| } |
| } |
| |
| // CHECK-LABEL: func.func @scalable_tile( |
| // CHECK-SAME: %[[ARG_0:.*]]: tensor<?xf32>, %[[ARG_1:.*]]: tensor<?xf32>, %[[ARG_2:.*]]: tensor<?xf32>, |
| // CHECK: %[[C0:.*]] = arith.constant 0 : index |
| // CHECK: %[[DIM:.*]] = tensor.dim %[[ARG_0]], %[[C0]] : tensor<?xf32> |
| // CHECK: %[[VEC_SIZE:.*]] = arith.constant 4 : index |
| // CHECK: %[[VS:.*]] = vector.vscale |
| // CHECK: %[[STEP:.*]] = arith.muli %[[VEC_SIZE]], %[[VS]] : index |
| // CHECK: scf.for %[[IV:.*]] = %[[C0]] to %[[DIM]] step %[[STEP]] iter_args(%[[VAL:.*]] = %[[ARG_2]]) -> (tensor<?xf32>) { |
| // CHECK: %[[SIZE:.*]] = affine.min affine_map<(d0)[s0, s1] -> (-d0 + s0, s1)>(%[[IV]])[%[[DIM]], %[[STEP]]] |
| // CHECK: %[[SLICE_ARG0:.*]] = tensor.extract_slice %[[ARG_0]][%[[IV]]] [%[[SIZE]]] [1] : tensor<?xf32> to tensor<?xf32> |
| // CHECK: %[[SLICE_ARG1:.*]] = tensor.extract_slice %[[ARG_1]][%[[IV]]] [%[[SIZE]]] [1] : tensor<?xf32> to tensor<?xf32> |
| // CHECK: %[[SLICE_ARG2:.*]] = tensor.extract_slice %[[VAL]][%[[IV]]] [%[[SIZE]]] [1] : tensor<?xf32> to tensor<?xf32> |
| // CHECK: linalg.generic {indexing_maps = {{.*}}, iterator_types = ["parallel"]} ins(%[[SLICE_ARG0]], %[[SLICE_ARG1]] : tensor<?xf32>, tensor<?xf32>) outs(%[[SLICE_ARG2]] : tensor<?xf32>) { |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.generic"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1, %loop = transform.structured.tile_using_for %0 tile_sizes [[4]] : (!transform.any_op) -> (!transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // ----- |
| |
| // CHECK-LABEL: func.func @scalable_and_fixed_length_tile |
| // CHECK-DAG: %[[C4:.*]] = arith.constant 4 : index |
| // CHECK-DAG: %[[VS:.*]] = vector.vscale |
| // CHECK-DAG: %[[STEP_2:.*]] = arith.muli %[[C4]], %[[VS]] : index |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 : index |
| // CHECK-DAG: %[[C128:.*]] = arith.constant 128 : index |
| // CHECK: scf.for %[[VAL_11:.*]] = %[[C0]] to %[[C128]] step %[[C4]] |
| // CHECK: scf.for %[[VAL_16:.*]] = %[[C0]] to %[[C128]] step %[[C4]] |
| // CHECK: scf.for %{{.*}} = %[[C0]] to %[[C128]] step %[[STEP_2]] |
| |
| func.func @scalable_and_fixed_length_tile( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| |
| return %0 : tensor<128x128xf32> |
| } |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [4, 4, [4]] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // ----- |
| |
| func.func @negative_too_many_tiles(%arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, |
| %arg2: tensor<128x128xf32>) -> tensor<128x128xf32> { |
| // expected-note @below {{target op}} |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-error @below {{too many tiles provided, expected at most 3 found 4}} |
| %1, %loops = transform.structured.tile_using_for %0 tile_sizes [1, 0, 0, 0] : (!transform.any_op) -> (!transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-error @below {{op expected number of loops to tile (3) to match number of `loops` results (1)}} |
| %1, %loops = transform.structured.tile_using_for %0 tile_sizes [4, 4, 4] : (!transform.any_op) -> (!transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_loop_result_arity_mismatch( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-error @below {{interchange length exceeds iteration space dimensions}} |
| %1, %loops:2 = transform.structured.tile_using_for %0 tile_sizes [8, 16] interchange = [0, 1, 2] |
| : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_interchange_length_exceeds_rank( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-error @below {{expects interchange values to be in range [0, 3), found: 3}} |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [8, 16, 4] interchange = [0, 3, 1] |
| : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_interchange_out_of_range( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-error @below {{found duplicate interchange value: 1}} |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [8, 16, 4] interchange = [0, 1, 1] |
| : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_interchange_duplicate_value( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %c3 = transform.param.constant 3 : i64 -> !transform.any_param |
| // expected-error @below {{expects interchange values to be in range [0, 3), found: 3}} |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [8, 16, 4] interchange = [0, %c3, 1] |
| : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_dynamic_valued_interchange_out_of_range( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %c0 = transform.param.constant 0 : i64 -> !transform.any_param |
| // expected-error @below {{found duplicate interchange value: 0}} |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [8, 16, 4] interchange = [0, %c0, 1] |
| : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_dynamic_valued_interchange_duplicate_value( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %c0a = transform.param.constant 0 : i64 -> !transform.any_param |
| %c0b = transform.param.constant 0 : i64 -> !transform.any_param |
| // expected-error @below {{found duplicate interchange value: 0}} |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [8, 16, 4] interchange = [%c0a, %c0b, 1] |
| : (!transform.any_op, !transform.any_param, !transform.any_param) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_dynamic_valued_interchange_duplicate_value_from_two_constant_params( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %packed = transform.test_produce_param (0.0 : f64) : !transform.any_param |
| // expected-error @below {{expected the parameter to be associated with an integer attribute}} |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [8, 16, 4] interchange = *(%packed) |
| : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_packed_interchange_non_integer_param( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| // expected-error @below {{op expected number of loops to tile (0) to match number of `loops` results (1)}} |
| %1, %loops = transform.structured.tile_using_for %0 tile_sizes [0, 0] : (!transform.any_op) -> (!transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| func.func @negative_tile_linalg_matmul_zero_tiles_loop_result_arity_mismatch( |
| %arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32>, %arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> { |
| %0 = linalg.matmul ins(%arg0, %arg1: tensor<128x128xf32>, tensor<128x128xf32>) |
| outs(%arg2: tensor<128x128xf32>) |
| -> tensor<128x128xf32> |
| return %0 : tensor<128x128xf32> |
| } |
| |
| // ----- |
| |
| // Tiling `linalg.scaled_contract` must respect the block-scaling scheme encoded in the scale |
| // indexing maps: along every iteration dimension that is block-scaled (`d floordiv B`), |
| // the tile size must divide or be divisible by the block factor `B`. |
| |
| // CHECK-LABEL: func.func @tile_scaled_contract_multiple_of_block_scale( |
| // CHECK-SAME: %[[A:[a-zA-Z0-9]+]]: tensor<256x512xi8> |
| // CHECK-SAME: %[[SCALE_A:[a-zA-Z0-9]+]]: tensor<8x4xf8E8M0FNU> |
| // CHECK-SAME: %[[B:[a-zA-Z0-9]+]]: tensor<128x512xi8> |
| // CHECK-SAME: %[[SCALE_B:[a-zA-Z0-9]+]]: tensor<128xf8E8M0FNU> |
| // CHECK-SAME: %[[C:[a-zA-Z0-9]+]]: tensor<256x128xf32> |
| // CHECK-DAG: %[[C16:.+]] = arith.constant 16 : index |
| // CHECK-DAG: %[[C32:.+]] = arith.constant 32 : index |
| // CHECK-DAG: %[[C128:.+]] = arith.constant 128 : index |
| // CHECK: scf.for %[[IV_M:[a-zA-Z0-9]+]] = %{{.+}} to %{{.+}} step %[[C32]] iter_args(%[[INIT_M:.+]] = %[[C]]) |
| // CHECK: scf.for %[[IV_N:[a-zA-Z0-9]+]] = %{{.+}} to %{{.+}} step %[[C16]] iter_args(%[[INIT_N:.+]] = %[[INIT_M]]) |
| // CHECK: scf.for %[[IV_K:[a-zA-Z0-9]+]] = %{{.+}} to %{{.+}} step %[[C128]] iter_args(%[[INIT_K:.+]] = %[[INIT_N]]) |
| // CHECK-DAG: %[[OFF_M:.+]] = affine.apply affine_map<(d0) -> (d0 floordiv 32)>(%[[IV_M]]) |
| // CHECK-DAG: %[[OFF_K:.+]] = affine.apply affine_map<(d0) -> (d0 floordiv 128)>(%[[IV_K]]) |
| // CHECK-DAG: %[[A_TILE:.+]] = tensor.extract_slice %[[A]][%[[IV_M]], %[[IV_K]]] [32, 128] [1, 1] |
| // CHECK-DAG: %[[SA_TILE:.+]] = tensor.extract_slice %[[SCALE_A]][%[[OFF_M]], %[[OFF_K]]] [1, 1] [1, 1] |
| // CHECK-DAG: %[[B_TILE:.+]] = tensor.extract_slice %[[B]][%[[IV_N]], %[[IV_K]]] [16, 128] [1, 1] |
| // CHECK-DAG: %[[SB_TILE:.+]] = tensor.extract_slice %[[SCALE_B]][%[[IV_N]]] [16] [1] |
| // CHECK-DAG: %[[C_TILE:.+]] = tensor.extract_slice %[[INIT_K]][%[[IV_M]], %[[IV_N]]] [32, 16] [1, 1] |
| // CHECK: %[[RES:.+]] = linalg.scaled_contract |
| // CHECK-SAME: ins(%[[A_TILE]], %[[SA_TILE]], %[[B_TILE]], %[[SB_TILE]] : |
| // CHECK-SAME: outs(%[[C_TILE]] : |
| // CHECK-SAME: -> tensor<32x16xf32> |
| // CHECK: tensor.insert_slice %[[RES]] into %[[INIT_K]][%[[IV_M]], %[[IV_N]]] [32, 16] [1, 1] |
| func.func @tile_scaled_contract_multiple_of_block_scale( |
| %A: tensor<256x512xi8>, %scaleA: tensor<8x4xf8E8M0FNU>, |
| %B: tensor<128x512xi8>, %scaleB: tensor<128xf8E8M0FNU>, |
| %C: tensor<256x128xf32>) -> tensor<256x128xf32> { |
| %D = linalg.scaled_contract |
| indexing_maps = [ |
| affine_map<(m, n, k) -> (m, k)>, |
| affine_map<(m, n, k) -> (m floordiv 32, k floordiv 128)>, |
| affine_map<(m, n, k) -> (n, k)>, |
| affine_map<(m, n, k) -> (n)>, |
| affine_map<(m, n, k) -> (m, n)>] |
| ins(%A, %scaleA, %B, %scaleB |
| : tensor<256x512xi8>, tensor<8x4xf8E8M0FNU>, tensor<128x512xi8>, tensor<128xf8E8M0FNU>) |
| outs(%C : tensor<256x128xf32>) -> tensor<256x128xf32> |
| return %D : tensor<256x128xf32> |
| } |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.scaled_contract"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [32, 16, 128] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // ----- |
| |
| // CHECK-LABEL: func.func @tile_scaled_contract_divisor_of_block_scale( |
| // CHECK-SAME: %[[A:[a-zA-Z0-9]+]]: tensor<256x512xi8> |
| // CHECK-SAME: %[[SCALE_A:[a-zA-Z0-9]+]]: tensor<8x4xf8E8M0FNU> |
| // CHECK: scf.for %[[IV_M:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK: scf.for %[[IV_N:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK: scf.for %[[IV_K:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK-DAG: %[[OFF_M:.+]] = affine.apply affine_map<(d0) -> (d0 floordiv 32)>(%[[IV_M]]) |
| // CHECK-DAG: %[[OFF_K:.+]] = affine.apply affine_map<(d0) -> (d0 floordiv 128)>(%[[IV_K]]) |
| // CHECK-DAG: %[[A_TILE:.+]] = tensor.extract_slice %[[A]][%[[IV_M]], %[[IV_K]]] [32, 64] [1, 1] |
| // CHECK-DAG: %[[SA_TILE:.+]] = tensor.extract_slice %[[SCALE_A]][%[[OFF_M]], %[[OFF_K]]] [1, 1] [1, 1] |
| // CHECK: %[[RES:.+]] = linalg.scaled_contract |
| // CHECK-SAME: -> tensor<32x16xf32> |
| func.func @tile_scaled_contract_divisor_of_block_scale( |
| %A: tensor<256x512xi8>, %scaleA: tensor<8x4xf8E8M0FNU>, |
| %B: tensor<128x512xi8>, %scaleB: tensor<128xf8E8M0FNU>, |
| %C: tensor<256x128xf32>) -> tensor<256x128xf32> { |
| %D = linalg.scaled_contract |
| indexing_maps = [ |
| affine_map<(m, n, k) -> (m, k)>, |
| affine_map<(m, n, k) -> (m floordiv 32, k floordiv 128)>, |
| affine_map<(m, n, k) -> (n, k)>, |
| affine_map<(m, n, k) -> (n)>, |
| affine_map<(m, n, k) -> (m, n)>] |
| ins(%A, %scaleA, %B, %scaleB |
| : tensor<256x512xi8>, tensor<8x4xf8E8M0FNU>, tensor<128x512xi8>, tensor<128xf8E8M0FNU>) |
| outs(%C : tensor<256x128xf32>) -> tensor<256x128xf32> |
| return %D : tensor<256x128xf32> |
| } |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.scaled_contract"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [32, 16, 64] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // ----- |
| |
| // Scalar (whole-tensor) scale on the LHS and per-dimension scale on the RHS |
| // impose no additional constraint, so the tile sizes can be arbitrary (and |
| // need not divide the operand shapes). |
| |
| // CHECK-LABEL: func.func @tile_scaled_contract_scalar_and_dimension_scale( |
| // CHECK-SAME: %[[A:[a-zA-Z0-9]+]]: tensor<256x100xi8> |
| // CHECK-SAME: %[[SCALE_A:[a-zA-Z0-9]+]]: tensor<f8E8M0FNU> |
| // CHECK: scf.for %[[IV_M:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK: scf.for %[[IV_N:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK: scf.for %[[IV_K:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK: %[[RES:.+]] = linalg.scaled_contract |
| // CHECK-SAME: ins(%{{.+}}, %[[SCALE_A]], %{{.+}}, %{{.+}} : |
| // CHECK-SAME: -> tensor<?x?xf32> |
| func.func @tile_scaled_contract_scalar_and_dimension_scale( |
| %A: tensor<256x100xi8>, %scaleA: tensor<f8E8M0FNU>, |
| %B: tensor<100x128xi8>, %scaleB: tensor<128xf8E8M0FNU>, |
| %C: tensor<256x128xf32>) -> tensor<256x128xf32> { |
| %D = linalg.scaled_contract |
| indexing_maps = [ |
| affine_map<(m, n, k) -> (m, k)>, |
| affine_map<(m, n, k) -> ()>, |
| affine_map<(m, n, k) -> (k, n)>, |
| affine_map<(m, n, k) -> (n)>, |
| affine_map<(m, n, k) -> (m, n)>] |
| ins(%A, %scaleA, %B, %scaleB |
| : tensor<256x100xi8>, tensor<f8E8M0FNU>, tensor<100x128xi8>, tensor<128xf8E8M0FNU>) |
| outs(%C : tensor<256x128xf32>) -> tensor<256x128xf32> |
| return %D : tensor<256x128xf32> |
| } |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.scaled_contract"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [30, 17, 25] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // ----- |
| |
| // Dynamic operand shapes with static, valid tile sizes. The constraint is |
| // checked against the (constant) upper bound of the tile size, so block-scaled |
| // tiling of dynamic tensors is allowed as long as the tile sizes divide or are |
| // divisible by the block factors. |
| // CHECK-LABEL: func.func @tile_scaled_contract_dynamic_shapes( |
| // CHECK: scf.for %[[IV_M:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK: scf.for %[[IV_N:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK: scf.for %[[IV_K:[a-zA-Z0-9]+]] = %{{.+}} step %{{.+}} |
| // CHECK: %[[RES:.+]] = linalg.scaled_contract |
| // CHECK-SAME: -> tensor<?x?xf32> |
| func.func @tile_scaled_contract_dynamic_shapes( |
| %A: tensor<?x?xi8>, %scaleA: tensor<?x?xf8E8M0FNU>, |
| %B: tensor<?x?xi8>, %scaleB: tensor<?xf8E8M0FNU>, |
| %C: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %D = linalg.scaled_contract |
| indexing_maps = [ |
| affine_map<(m, n, k) -> (m, k)>, |
| affine_map<(m, n, k) -> (m floordiv 32, k floordiv 128)>, |
| affine_map<(m, n, k) -> (n, k)>, |
| affine_map<(m, n, k) -> (n)>, |
| affine_map<(m, n, k) -> (m, n)>] |
| ins(%A, %scaleA, %B, %scaleB |
| : tensor<?x?xi8>, tensor<?x?xf8E8M0FNU>, tensor<?x?xi8>, tensor<?xf8E8M0FNU>) |
| outs(%C : tensor<?x?xf32>) -> tensor<?x?xf32> |
| return %D : tensor<?x?xf32> |
| } |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.scaled_contract"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [32, 16, 128] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |
| |
| // ----- |
| |
| func.func @negative_tile_scaled_contract_non_divisible_block_scale( |
| %A: tensor<256x512xi8>, %scaleA: tensor<8x4xf8E8M0FNU>, |
| %B: tensor<128x512xi8>, %scaleB: tensor<128xf8E8M0FNU>, |
| %C: tensor<256x128xf32>) -> tensor<256x128xf32> { |
| // expected-error @below {{'linalg.scaled_contract' op tiling is not supported for the semi-affine indexing map: tile size 48 for dimension d0 must divide or be divisible by the step 32}} |
| // expected-error @below {{'linalg.scaled_contract' op failed to tile operation}} |
| // expected-error @below {{'linalg.scaled_contract' op failed to generate tiling loops}} |
| %D = linalg.scaled_contract |
| indexing_maps = [ |
| affine_map<(m, n, k) -> (m, k)>, |
| affine_map<(m, n, k) -> (m floordiv 32, k floordiv 128)>, |
| affine_map<(m, n, k) -> (n, k)>, |
| affine_map<(m, n, k) -> (n)>, |
| affine_map<(m, n, k) -> (m, n)>] |
| ins(%A, %scaleA, %B, %scaleB |
| : tensor<256x512xi8>, tensor<8x4xf8E8M0FNU>, tensor<128x512xi8>, tensor<128xf8E8M0FNU>) |
| outs(%C : tensor<256x128xf32>) -> tensor<256x128xf32> |
| return %D : tensor<256x128xf32> |
| } |
| module attributes {transform.with_named_sequence} { |
| transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) { |
| %0 = transform.structured.match ops{["linalg.scaled_contract"]} in %arg1 : (!transform.any_op) -> !transform.any_op |
| %1, %loops:3 = transform.structured.tile_using_for %0 tile_sizes [48, 16, 128] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op) |
| transform.yield |
| } |
| } |