726 lines · plain
1// RUN: mlir-opt %s --transform-interpreter -canonicalize -cse -split-input-file -verify-diagnostics | FileCheck %s2 3// Offset per thread:4// CHECK-DAG: affine_map<(d0)[s0] -> (d0 * (s0 ceildiv 10))>5// Per thread tile size.6// CHECK-DAG: affine_map<(d0)[s0] -> (-(d0 * (s0 ceildiv 10)) + s0, s0 ceildiv 10)>7// CHECK-DAG: affine_map<(d0)[s0] -> (d0 * (s0 ceildiv 20))>8// CHECK-DAG: affine_map<(d0)[s0] -> (-(d0 * (s0 ceildiv 20)) + s0, s0 ceildiv 20)>9 10module {11// CHECK-LABEL: matmul(12// CHECK-SAME: %[[A:[0-9a-z]+]]: tensor<?x?xf32>13// CHECK-SAME: %[[B:[0-9a-z]+]]: tensor<?x?xf32>14// CHECK-SAME: %[[C:[0-9a-z]+]]: tensor<?x?xf32>15 func.func @matmul(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {16 // CHECK: scf.forall ({{.*}}) in (10, 20) shared_outs(%[[C_BLK:.*]] = %[[C]]) -> (tensor<?x?xf32>) {17 // CHECK: %[[tA:.*]] = tensor.extract_slice %[[A]]{{.*}} : tensor<?x?xf32> to tensor<?x?xf32>18 // CHECK: %[[tB:.*]] = tensor.extract_slice %[[B]]{{.*}} : tensor<?x?xf32> to tensor<?x?xf32>19 // CHECK: %[[tC:.*]] = tensor.extract_slice %[[C_BLK]]{{.*}} : tensor<?x?xf32> to tensor<?x?xf32>20 // CHECK: %[[RES:.*]] = linalg.matmul21 // CHECK-SAME: ins(%[[tA]], %[[tB]] : tensor<?x?xf32>, tensor<?x?xf32>)22 // CHECK-SAME: outs(%[[tC]] : tensor<?x?xf32>) -> tensor<?x?xf32>23 // CHECK: scf.forall.in_parallel {24 // CHECK-NEXT: tensor.parallel_insert_slice %[[RES]] into %[[C_BLK]]{{.*}} :25 // CHECK-SAME: tensor<?x?xf32> into tensor<?x?xf32>26 // CHECK-NEXT: }27 // CHECK-NEXT: } {mapping = [#gpu.thread<y>, #gpu.thread<x>]}28 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)29 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)30 return %0 : tensor<?x?xf32>31 }32 33 module attributes {transform.with_named_sequence} {34 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {35 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op36 %1:2 = transform.structured.tile_using_forall %0 num_threads [10, 20] (mapping = [ #gpu.thread<y>, #gpu.thread<x> ] )37 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)38 transform.yield39 }40 }41}42 43// -----44 45module {46 // CHECK-LABEL: func @matmul_memref(47 // CHECK: scf.forall (%{{.*}}, %{{.*}}) in (10, 20) {48 // CHECK: memref.subview49 // CHECK: memref.subview50 // CHECK: memref.subview51 // CHECK: linalg.matmul52 // CHECK: } {mapping = [#gpu.thread<y>, #gpu.thread<x>]}53 func.func @matmul_memref(%A: memref<?x?xf32>, %B: memref<?x?xf32>, %C: memref<?x?xf32>) {54 linalg.matmul ins(%A, %B : memref<?x?xf32>, memref<?x?xf32>)55 outs(%C : memref<?x?xf32>)56 return57 }58 59 module attributes {transform.with_named_sequence} {60 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {61 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op62 %1:2 = transform.structured.tile_using_forall %0 num_threads [10, 20] (mapping = [ #gpu.thread<y>, #gpu.thread<x> ] )63 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)64 transform.yield65 }66 }67}68 69// -----70 71module {72 // CHECK-LABEL: func @copy_memref(73 // CHECK: scf.forall (%{{.*}}, %{{.*}}) in (10, 20) {74 // CHECK: memref.subview75 // CHECK: memref.subview76 // CHECK: linalg.copy77 // CHECK: } {mapping = [#gpu.thread<y>, #gpu.thread<x>]}78 func.func @copy_memref(%A: memref<?x?xf32>, %B: memref<?x?xf32>) {79 linalg.copy ins(%A: memref<?x?xf32>)80 outs(%B : memref<?x?xf32>)81 return82 }83 84 module attributes {transform.with_named_sequence} {85 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {86 %0 = transform.structured.match ops{["linalg.copy"]} in %arg1 : (!transform.any_op) -> !transform.any_op87 %1:2 = transform.structured.tile_using_forall %0 num_threads [10, 20] (mapping = [ #gpu.thread<y>, #gpu.thread<x> ] )88 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)89 transform.yield90 }91 }92}93 94// -----95 96// In this test case, matmul dims and tile size are dynamic.97 98// CHECK-DAG: #[[$map0:.+]] = affine_map<()[s0, s1] -> (s0 ceildiv s1)>99// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0)[s0, s1] -> (-(d0 * s1) + s0, s1)>100// CHECK-DAG: #[[$map4:.+]] = affine_map<(d0)[s0] -> (d0 * s0)>101 102// CHECK-LABEL: matmul_tile_size_dynamic_dynamic(103// CHECK-SAME: %[[A:[0-9a-z]+]]: tensor<?x?xf32>104// CHECK-SAME: %[[B:[0-9a-z]+]]: tensor<?x?xf32>105// CHECK-SAME: %[[C:[0-9a-z]+]]: tensor<?x?xf32>106func.func @matmul_tile_size_dynamic_dynamic(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {107 // CHECK-DAG: %[[c0:.*]] = arith.constant 0 : index108 // CHECK-DAG: %[[c1:.*]] = arith.constant 1 : index109 // CHECK-DAG: %[[tile_size_1:.*]] = "test.dummy"()110 // CHECK-DAG: %[[tile_size_2:.*]] = "test.dummy"()111 // CHECK-DAG: %[[M:.+]] = tensor.dim %[[A]], %[[c0]] :112 // CHECK-DAG: %[[N:.+]] = tensor.dim %[[B]], %c1 :113 // CHECK-DAG: %[[NT0:.+]] = affine.apply #[[$map0]]()[%[[M]], %[[tile_size_1]]]114 // CHECK-DAG: %[[NT1:.+]] = affine.apply #[[$map0]]()[%[[N]], %[[tile_size_2]]]115 // CHECK: scf.forall (%[[IV0:.+]], %[[IV1:.+]]) in (%[[NT0]], %[[NT1]]) shared_outs(%[[C_BLK:.*]] = %[[C]])116 // CHECK: tensor.extract_slice %[[A]]117 // CHECK: tensor.extract_slice %[[B]]118 // CHECK: tensor.extract_slice %[[C_BLK]]119 // CHECK: linalg.matmul120 // CHECK: scf.forall.in_parallel121 // CHECK-NEXT: tensor.parallel_insert_slice122 %tile_size_1 = "test.dummy"() : () -> (index)123 %tile_size_2 = "test.dummy"() : () -> (index)124 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)125 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)126 return %0 : tensor<?x?xf32>127}128 129module attributes {transform.with_named_sequence} {130 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {131 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op132 %sz = transform.structured.match ops{["test.dummy"]} in %arg1 : (!transform.any_op) -> !transform.any_op133 %1:2 = transform.structured.tile_using_forall %0 tile_sizes *(%sz)134 : (!transform.any_op, !transform.any_op) -> (!transform.any_op, !transform.any_op)135 transform.yield136 }137}138 139// -----140 141// Tests that dimension 0 can eliminate affine.min/max, dimension 1 cannot.142 143// CHECK-DAG: #[[$map0:.+]] = affine_map<(d0) -> (d0 * -15 + 300, 15)>144// CHECK-DAG: #[[$map1:.+]] = affine_map<(d0) -> (0, d0)>145// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0) -> (d0 * 10)>146// CHECK-DAG: #[[$map3:.+]] = affine_map<(d0) -> (d0 * 15)>147 148// CHECK-LABEL: matmul_static(149// CHECK-SAME: %[[A:[0-9a-z]+]]: tensor150// CHECK-SAME: %[[B:[0-9a-z]+]]: tensor151// CHECK-SAME: %[[C:[0-9a-z]+]]: tensor152func.func @matmul_static(%A: tensor<100x200xf32>, %B: tensor<200x300xf32>, %C: tensor<100x300xf32>) -> tensor<100x300xf32> {153 // CHECK: scf.forall (%[[IV0:.+]], %[[IV1:.+]]) in (10, 21) shared_outs(%[[C_BLK:.*]] = %[[C]])154 // CHECK: %[[TSMIN:.+]] = affine.min #[[$map0]](%[[IV1]])155 // CHECK: %[[TS:.+]] = affine.max #[[$map1]](%[[TSMIN]])156 // CHECK-NOT: affine.min157 // CHECK-NOT: affine.max158 // CHECK: %[[LB0:.+]] = affine.apply #[[$map2]](%[[IV0]])159 // CHECK: %[[LB1:.+]] = affine.apply #[[$map3]](%[[IV1]])160 // CHECK: %[[tA:.+]] = tensor.extract_slice %[[A]][%[[LB0]], 0] [10, 200] [1, 1] :161 // CHECK: %[[tB:.+]] = tensor.extract_slice %[[B]][0, %[[LB1]]] [200, %[[TS]]] [1, 1] :162 // CHECK: %[[tC:.+]] = tensor.extract_slice %[[C_BLK]][%[[LB0]], %[[LB1]]] [10, %[[TS]]] [1, 1] :163 // CHECK: linalg.matmul164 // CHECK: scf.forall.in_parallel165 // CHECK-NEXT: tensor.parallel_insert_slice166 %0 = linalg.matmul ins(%A, %B : tensor<100x200xf32>, tensor<200x300xf32>)167 outs(%C : tensor<100x300xf32>) -> (tensor<100x300xf32>)168 return %0 : tensor<100x300xf32>169}170 171module attributes {transform.with_named_sequence} {172 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {173 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op174 %1:2 = transform.structured.tile_using_forall %0 num_threads [10, 21]175 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)176 transform.yield177 }178}179 180// -----181 182// CHECK-DAG: #[[$map0:.+]] = affine_map<()[s0] -> (s0 ceildiv 10)>183// CHECK-DAG: #[[$map1:.+]] = affine_map<()[s0] -> (s0 ceildiv 20)>184// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0)[s0] -> (d0 * -10 + s0, 10)>185// CHECK-DAG: #[[$map4:.+]] = affine_map<(d0)[s0] -> (d0 * -20 + s0, 20)>186// CHECK-DAG: #[[$map5:.+]] = affine_map<(d0) -> (d0 * 10)>187// CHECK-DAG: #[[$map6:.+]] = affine_map<(d0) -> (d0 * 20)>188 189// CHECK-LABEL: matmul_tile_size_dynamic(190// CHECK-SAME: %[[A:[0-9a-z]+]]: tensor<?x?xf32>191// CHECK-SAME: %[[B:[0-9a-z]+]]: tensor<?x?xf32>192// CHECK-SAME: %[[C:[0-9a-z]+]]: tensor<?x?xf32>193func.func @matmul_tile_size_dynamic(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {194 // CHECK: %[[M:.+]] = tensor.dim %[[A]], %c0 :195 // CHECK: %[[N:.+]] = tensor.dim %[[B]], %c1 :196 // CHECK: %[[NT0:.+]] = affine.apply #[[$map0]]()[%[[M]]]197 // CHECK: %[[NT1:.+]] = affine.apply #[[$map1]]()[%[[N]]]198 // CHECK: scf.forall (%[[IV0:.+]], %[[IV1:.+]]) in (%[[NT0]], %[[NT1]]) shared_outs(%[[C_BLK:.*]] = %[[C]])199 // CHECK-DAG: %[[TS0:.+]] = affine.min #[[$map2]](%[[IV0]])[%[[M]]]200 // CHECK-DAG: %[[TS1:.+]] = affine.min #[[$map4]](%[[IV1]])[%[[N]]]201 // CHECK-DAG: %[[LB0:.+]] = affine.apply #[[$map5]](%[[IV0]])202 // CHECK-DAG: %[[LB1:.+]] = affine.apply #[[$map6]](%[[IV1]])203 // CHECK: tensor.extract_slice %[[A]]204 // CHECK: tensor.extract_slice %[[B]]205 // CHECK: tensor.extract_slice %[[C_BLK]]206 // CHECK: linalg.matmul207 // CHECK: scf.forall.in_parallel208 // CHECK-NEXT: tensor.parallel_insert_slice209 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)210 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)211 return %0 : tensor<?x?xf32>212}213 214module attributes {transform.with_named_sequence} {215 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {216 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op217 %1:2 = transform.structured.tile_using_forall %0 tile_sizes [10, 20]218 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)219 transform.yield220 }221}222// -----223 224// Tests that dimension 0 can eliminate affine.min/max, dimension 1 cannot.225 226// CHECK-DAG: #[[$map0:.+]] = affine_map<(d0) -> (d0 * -21 + 300, 21)>227// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0) -> (d0 * 10)>228// CHECK-DAG: #[[$map3:.+]] = affine_map<(d0) -> (d0 * 21)>229 230// CHECK-LABEL: matmul_tile_size_static(231// CHECK-SAME: %[[A:[0-9a-z]+]]: tensor232// CHECK-SAME: %[[B:[0-9a-z]+]]: tensor233// CHECK-SAME: %[[C:[0-9a-z]+]]: tensor234func.func @matmul_tile_size_static(%A: tensor<100x200xf32>, %B: tensor<200x300xf32>, %C: tensor<100x300xf32>) -> tensor<100x300xf32> {235 // CHECK: scf.forall (%[[IV0:.+]], %[[IV1:.+]]) in (10, 15) shared_outs(%[[C_BLK:.*]] = %[[C]])236 // CHECK-DAG: %[[TS:.+]] = affine.min #[[$map0]](%[[IV1]])237 // CHECK-DAG: %[[LB0:.+]] = affine.apply #[[$map2]](%[[IV0]])238 // CHECK-DAG: %[[LB1:.+]] = affine.apply #[[$map3]](%[[IV1]])239 // CHECK-NOT: affine.max240 // CHECK-NOT: affine.min241 // CHECK: %[[tA:.+]] = tensor.extract_slice %[[A]][%[[LB0]], 0] [10, 200] [1, 1] :242 // CHECK: %[[tB:.+]] = tensor.extract_slice %[[B]][0, %[[LB1]]] [200, %[[TS]]] [1, 1] :243 // CHECK: %[[tC:.+]] = tensor.extract_slice %[[C_BLK]][%[[LB0]], %[[LB1]]] [10, %[[TS]]] [1, 1] :244 // CHECK: linalg.matmul245 // CHECK: scf.forall.in_parallel246 // CHECK-NEXT: tensor.parallel_insert_slice247 %0 = linalg.matmul ins(%A, %B : tensor<100x200xf32>, tensor<200x300xf32>)248 outs(%C : tensor<100x300xf32>) -> (tensor<100x300xf32>)249 return %0 : tensor<100x300xf32>250}251 252module attributes {transform.with_named_sequence} {253 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {254 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op255 %1:2 = transform.structured.tile_using_forall %0 tile_sizes [10, 21]256 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)257 transform.yield258 }259}260 261// -----262 263module {264 func.func @extract_source(%A: tensor<4xf32>, %B: tensor<16xf32>) -> tensor<4xf32> {265 %B1 = tensor.extract_slice %B[10] [4] [1] : tensor<16xf32> to tensor<4xf32>266 %result = linalg.generic {indexing_maps = [267 affine_map<(d0) -> (d0)>,affine_map<(d0) -> (d0)>],268 iterator_types = ["parallel"]}269 ins(%A : tensor<4xf32>) outs(%B1 : tensor<4xf32>) {270 ^bb0(%arg3: f32, %arg4: f32): // no predecessors271 %2 = arith.addf %arg3, %arg3 : f32272 linalg.yield %2 : f32273 } -> tensor<4xf32>274 return %result : tensor<4xf32>275 }276 277 module attributes {transform.with_named_sequence} {278 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {279 %0 = transform.structured.match ops{["linalg.generic"]} in %arg1 : (!transform.any_op) -> !transform.any_op280 %1:2 = transform.structured.tile_using_forall %0 num_threads [2] ( mapping = [#gpu.thread<x>])281 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)282 transform.yield283 }284 }285}286// CHECK-DAG: #[[$map0:.+]] = affine_map<(d0) -> (d0 * 2)>287 288// CHECK-LABEL: extract_source(289// CHECK: scf.forall (%[[ARG:.*]]) in (2) shared_outs(%{{.*}} = %{{.*}}) -> (tensor<4xf32>) {290// CHECK: %[[OFF:.*]] = affine.apply #[[$map0]](%[[ARG]])291// CHECK: scf.forall.in_parallel {292// CHECK: tensor.parallel_insert_slice %{{.*}} into %{{.*}}[%[[OFF]]] [2] [1] : tensor<2xf32> into tensor<4xf32>293 294// -----295 296// In this test case, matmul dims and tile size are dynamic.297 298// CHECK-DAG: #[[$map0:.+]] = affine_map<()[s0, s1] -> (s0 ceildiv s1)>299// CHECK-DAG: #[[$map1:.+]] = affine_map<()[s0] -> (s0 ceildiv 20)>300// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0)[s0, s1] -> (-(d0 * s1) + s0, s1)>301// CHECK-DAG: #[[$map3:.+]] = affine_map<(d0)[s0] -> (d0 * -20 + s0, 20)>302// CHECK-DAG: #[[$map4:.+]] = affine_map<(d0)[s0] -> (d0 * s0)>303// CHECK-DAG: #[[$map5:.+]] = affine_map<(d0) -> (d0 * 20)>304 305// CHECK-LABEL: matmul_tile_size_dynamic_dynamic(306// CHECK-SAME: %[[A:[0-9a-z]+]]: tensor<?x?xf32>307// CHECK-SAME: %[[B:[0-9a-z]+]]: tensor<?x?xf32>308// CHECK-SAME: %[[C:[0-9a-z]+]]: tensor<?x?xf32>309func.func @matmul_tile_size_dynamic_dynamic(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {310 // CHECK-DAG: %[[c0:.*]] = arith.constant 0 : index311 // CHECK-DAG: %[[c1:.*]] = arith.constant 1 : index312 // CHECK-DAG: %[[tile_size:.*]] = "test.dummy"()313 // CHECK-DAG: %[[M:.+]] = tensor.dim %[[A]], %[[c0]] :314 // CHECK-DAG: %[[N:.+]] = tensor.dim %[[B]], %c1 :315 // CHECK-DAG: %[[NT0:.+]] = affine.apply #[[$map0]]()[%[[M]], %[[tile_size]]]316 // CHECK-DAG: %[[NT1:.+]] = affine.apply #[[$map1]]()[%[[N]]]317 // CHECK: scf.forall (%[[IV0:.+]], %[[IV1:.+]]) in (%[[NT0]], %[[NT1]]) shared_outs(%[[C_BLK:.*]] = %[[C]])318 // CHECK: tensor.extract_slice %[[A]]319 // CHECK: tensor.extract_slice %[[B]]320 // CHECK: tensor.extract_slice %[[C_BLK]]321 // CHECK: linalg.matmul322 // CHECK: scf.forall.in_parallel323 // CHECK-NEXT: tensor.parallel_insert_slice324 %tile_size = "test.dummy"() : () -> (index)325 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)326 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)327 return %0 : tensor<?x?xf32>328}329 330module attributes {transform.with_named_sequence} {331 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {332 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op333 %sz = transform.structured.match ops{["test.dummy"]} in %arg1 : (!transform.any_op) -> !transform.any_op334 %1:2 = transform.structured.tile_using_forall %0 tile_sizes [%sz, 20]335 : (!transform.any_op, !transform.any_op) -> (!transform.any_op, !transform.any_op)336 transform.yield337 }338}339 340// -----341 342// CHECK-DAG: #[[$map0:.+]] = affine_map<(d0) -> (d0 * -15 + 100, 15)>343// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0) -> (d0 * 15)>344// CHECK-DAG: #[[$map3:.+]] = affine_map<(d0) -> (d0)>345 346// CHECK-LABEL: tile_output_multi_1d_static(347// CHECK-SAME: %[[IN1:[0-9a-z]+]]: tensor<100xf32>348// CHECK-SAME: %[[IN2:[0-9a-z]+]]: tensor<100xf32>349// CHECK-SAME: %[[ORGOUT1:[0-9a-z]+]]: tensor<100xf32>350// CHECK-SAME: %[[ORGOUT2:[0-9a-z]+]]: tensor<100xf32>351 func.func @tile_output_multi_1d_static(%IN1: tensor<100xf32>, %IN2: tensor<100xf32>,352 %OUT1: tensor<100xf32>, %OUT2: tensor<100xf32>)353 -> (tensor<100xf32>, tensor<100xf32>) {354// CHECK: scf.forall (%[[IV0:.+]]) in (7) shared_outs(%[[OUT1:[0-9a-z]+]] = %[[ORGOUT1]], %[[OUT2:[0-9a-z]+]] = %[[ORGOUT2]])355// CHECK: %[[TS:.+]] = affine.min #[[$map0]](%[[IV0]])356// CHECK-NOT: affine.min357// CHECK-NOT: affine.max358// CHECK: %[[LB:.+]] = affine.apply #[[$map2]](%[[IV0]])359// CHECK: %[[tIN1:.+]] = tensor.extract_slice %[[IN1]][%[[LB]]] [%[[TS]]] [1] :360// CHECK: %[[tIN2:.+]] = tensor.extract_slice %[[IN2]][%[[LB]]] [%[[TS]]] [1] :361// CHECK: %[[tOUT1:.+]] = tensor.extract_slice %[[OUT1]][%[[LB]]] [%[[TS]]] [1] :362// CHECK: %[[tOUT2:.+]] = tensor.extract_slice %[[OUT2]][%[[LB]]] [%[[TS]]] [1] :363// CHECK: %[[RES1:[0-9]+]]:[[RES2:[0-9]+]] = linalg.generic364// CHECK: scf.forall.in_parallel365// CHECK-NEXT: tensor.parallel_insert_slice %[[RES1]]#0 into %[[OUT1]][%[[LB]]] [%[[TS]]] [1] :366// CHECK-NEXT: tensor.parallel_insert_slice %[[RES1]]#1 into %[[OUT2]][%[[LB]]] [%[[TS]]] [1] :367 %res1, %res2 = linalg.generic368 {369 indexing_maps = [affine_map<(d0) -> (d0)>,370 affine_map<(d0) -> (d0)>,371 affine_map<(d0) -> (d0)>,372 affine_map<(d0) -> (d0)>],373 iterator_types = ["parallel"]374 } ins(%IN1, %IN2 : tensor<100xf32>, tensor<100xf32>)375 outs(%OUT1, %OUT2 : tensor<100xf32>, tensor<100xf32>)376 {377 ^bb0(%a1: f32, %a2: f32, %a3: f32, %a4: f32):378 %1 = arith.addf %a1, %a3 : f32379 %2 = arith.addf %a2, %a4 : f32380 linalg.yield %1, %2 : f32,f32381 } -> (tensor<100xf32>, tensor<100xf32>)382 return %res1, %res2 : tensor<100xf32>, tensor<100xf32>383 }384 385 module attributes {transform.with_named_sequence} {386 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {387 %0 = transform.structured.match ops{["linalg.generic"]} in %arg1 : (!transform.any_op) -> !transform.any_op388 %tiled_generic, %forall = transform.structured.tile_using_forall %0 num_threads [7]389 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)390 transform.yield391 }392 }393 394// -----395 396// CHECK-DAG: #[[$map0:.+]] = affine_map<(d0) -> (d0 * 75)>397// CHECK-DAG: #[[$map1:.+]] = affine_map<(d0, d1) -> (d1)>398// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0, d1) -> (d1, d0)399// CHECK-DAG: #[[$map3:.+]] = affine_map<(d0, d1) -> (d0)>400// CHECK-DAG: #[[$map4:.+]] = affine_map<(d0, d1) -> (d0, d1)>401 402// CHECK-LABEL: tile_output_multi_1d2d_static(403// CHECK-SAME: %[[IN1:[0-9a-z]+]]: tensor<100xf32>404// CHECK-SAME: %[[IN2:[0-9a-z]+]]: tensor<100x300xf32>405// CHECK-SAME: %[[IN3:[0-9a-z]+]]: tensor<300xf32>406// CHECK-SAME: %[[ORGOUT1:[0-9a-z]+]]: tensor<300x100xf32>407// CHECK-SAME: %[[ORGOUT2:[0-9a-z]+]]: tensor<300xf32>408 func.func @tile_output_multi_1d2d_static(%IN1: tensor<100xf32>, %IN2: tensor<100x300xf32>, %IN3: tensor<300xf32>,409 %OUT1: tensor<300x100xf32>, %OUT2: tensor<300xf32>)410 -> (tensor<300x100xf32>, tensor<300xf32>) {411// CHECK: scf.forall (%[[IV0:.+]]) in (4) shared_outs(%[[OUT1:[0-9a-z]+]] = %[[ORGOUT1]], %[[OUT2:[0-9a-z]+]] = %[[ORGOUT2]])412// CHECK: %[[LB:.+]] = affine.apply #[[$map0]](%[[IV0]])413// CHECK: %[[tIN1:.+]] = tensor.extract_slice %[[IN2]][0, %[[LB]]] [100, 75]414// CHECK: %[[tIN2:.+]] = tensor.extract_slice %[[IN3]][%[[LB]]] [75]415// CHECK: %[[tOUT1:.+]] = tensor.extract_slice %[[OUT1]][%[[LB]], 0] [75, 100]416// CHECK: %[[tOUT2:.+]] = tensor.extract_slice %[[OUT2]][%[[LB]]] [75]417// CHECK: %[[RES1:[0-9]+]]:[[RES2:[0-9]+]] = linalg.generic418// CHECK: scf.forall.in_parallel419// CHECK-NEXT: tensor.parallel_insert_slice %[[RES1]]#0 into %[[OUT1]][%[[LB]], 0] [75, 100]420// CHECK-NEXT: tensor.parallel_insert_slice %[[RES1]]#1 into %[[OUT2]][%[[LB]]] [75]421 %res2, %res3 = linalg.generic {422 indexing_maps = [affine_map<(d0,d1) -> (d1)>,423 affine_map<(d0,d1) -> (d1,d0)>,424 affine_map<(d0,d1) -> (d0)>,425 affine_map<(d0,d1) -> (d0,d1)>,426 affine_map<(d0,d1) -> (d0)>427 ],428 iterator_types = ["parallel", "parallel"]429 } ins(%IN1, %IN2, %IN3 : tensor<100xf32>, tensor<100x300xf32>, tensor<300xf32>)430 outs(%OUT1, %OUT2: tensor<300x100xf32>, tensor<300xf32>) {431 ^bb0(%i1: f32, %i2: f32, %i3: f32, %o1: f32, %o2: f32):432 %1 = arith.addf %i1, %o1 : f32433 %2 = arith.addf %i2, %1 : f32434 %3 = arith.addf %i3, %2 : f32435 linalg.yield %3, %i3 : f32, f32436 } -> (tensor<300x100xf32>, tensor<300xf32>)437 438 return %res2, %res3 : tensor<300x100xf32>, tensor<300xf32>439 }440 441 module attributes {transform.with_named_sequence} {442 transform.named_sequence @__transform_main(%IN_MAT2: !transform.any_op {transform.readonly}) {443 %0 = transform.structured.match ops{["linalg.generic"]} in %IN_MAT2 : (!transform.any_op) -> !transform.any_op444 %tiled_generic, %forall = transform.structured.tile_using_forall %0 num_threads [4]445 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)446 transform.yield447 }448 }449 450// -----451 452// CHECK-DAG: #[[$map0:.+]] = affine_map<()[s0] -> (s0 ceildiv 10)>453// CHECK-DAG: #[[$map1:.+]] = affine_map<()[s0] -> (s0 ceildiv 20)>454// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0)[s0] -> (d0 * -10 + s0, 10)>455// CHECK-DAG: #[[$map3:.+]] = affine_map<(d0)[s0] -> (d0 * -20 + s0, 20)>456// CHECK-DAG: #[[$map4:.+]] = affine_map<(d0) -> (d0 * 10)>457// CHECK-DAG: #[[$map5:.+]] = affine_map<(d0) -> (d0 * 20)>458 459// CHECK-LABEL: matmul_tile_size_dynamic(460// CHECK-SAME: %[[A:[0-9a-z]+]]: tensor<?x?xf32>461// CHECK-SAME: %[[B:[0-9a-z]+]]: tensor<?x?xf32>462// CHECK-SAME: %[[C:[0-9a-z]+]]: tensor<?x?xf32>463func.func @matmul_tile_size_dynamic(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {464 // CHECK: %[[c1:.*]] = arith.constant 1 : index465 // CHECK: %[[c0:.*]] = arith.constant 0 : index466 // CHECK-DAG: %[[M:.+]] = tensor.dim %[[A]], %[[c0]] :467 // CHECK-DAG: %[[N:.+]] = tensor.dim %[[B]], %[[c1]] :468 // CHECK-DAG: %[[NT0:.+]] = affine.apply #map()[%[[M]]]469 // CHECK-DAG: %[[NT1:.+]] = affine.apply #map1()[%[[N]]]470 // CHECK-DAG: %[[K:.+]] = tensor.dim %[[A]], %[[c1]] :471 // CHECK: scf.forall (%[[IV0:.+]], %[[IV1:.+]]) in (%[[NT0]], %[[NT1]]) shared_outs(%[[C_BLK:.*]] = %[[C]])472 // CHECK-DAG: %[[TS0:.+]] = affine.min #[[$map2]](%[[IV0]])[%[[M]]]473 // CHECK-DAG: %[[TS1:.+]] = affine.min #[[$map3]](%[[IV1]])[%[[N]]]474 // CHECK-DAG: %[[LB0:.+]] = affine.apply #[[$map4]](%[[IV0]])475 // CHECK-DAG: %[[LB1:.+]] = affine.apply #[[$map5]](%[[IV1]])476 // CHECK: tensor.extract_slice %[[A]][%[[LB0]], 0] [%[[TS0]], %[[K]]] [1, 1] :477 // CHECK: tensor.extract_slice %[[B]][0, %[[LB1]]] [%[[K]], %[[TS1]]] [1, 1] :478 // CHECK: tensor.extract_slice %[[C_BLK]][%[[LB0]], %[[LB1]]] [%[[TS0]], %[[TS1]]] [1, 1] :479 // CHECK: linalg.matmul480 // CHECK: scf.forall.in_parallel481 // CHECK-NEXT: tensor.parallel_insert_slice482 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)483 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)484 return %0 : tensor<?x?xf32>485}486 487module attributes {transform.with_named_sequence} {488 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {489 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op490 %sz = transform.param.constant 10 : i64 -> !transform.param<i64>491 %1:2 = transform.structured.tile_using_forall %0 tile_sizes [%sz, 20]492 : (!transform.any_op, !transform.param<i64>) -> (!transform.any_op, !transform.any_op)493 transform.yield494 }495}496 497// -----498 499func.func @matmul_tile_size_dynamic(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {500 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)501 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)502 return %0 : tensor<?x?xf32>503}504 505module attributes {transform.with_named_sequence} {506 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {507 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op508 %c10 = transform.param.constant 10 : i64 -> !transform.param<i64>509 %c20 = transform.param.constant 20 : i64 -> !transform.param<i64>510 %sz = transform.merge_handles %c10, %c20 : !transform.param<i64>511 // expected-error @below {{requires exactly one parameter associated}}512 %1:2 = transform.structured.tile_using_forall %0 tile_sizes [%sz, 20]513 : (!transform.any_op, !transform.param<i64>) -> (!transform.any_op, !transform.any_op)514 transform.yield515 }516}517 518// -----519 520// CHECK-DAG: #[[$map0:.+]] = affine_map<()[s0] -> (s0 ceildiv 10)>521// CHECK-DAG: #[[$map1:.+]] = affine_map<()[s0] -> (s0 ceildiv 20)>522// CHECK-DAG: #[[$map2:.+]] = affine_map<(d0)[s0] -> (d0 * -10 + s0, 10)>523// CHECK-DAG: #[[$map3:.+]] = affine_map<(d0)[s0] -> (d0 * -20 + s0, 20)>524// CHECK-DAG: #[[$map4:.+]] = affine_map<(d0) -> (d0 * 10)>525// CHECK-DAG: #[[$map5:.+]] = affine_map<(d0) -> (d0 * 20)>526 527// CHECK-LABEL: matmul_tile_size_dynamic(528// CHECK-SAME: %[[A:[0-9a-z]+]]: tensor<?x?xf32>529// CHECK-SAME: %[[B:[0-9a-z]+]]: tensor<?x?xf32>530// CHECK-SAME: %[[C:[0-9a-z]+]]: tensor<?x?xf32>531func.func @matmul_tile_size_dynamic(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {532 // CHECK: %[[c1:.*]] = arith.constant 1 : index533 // CHECK: %[[c0:.*]] = arith.constant 0 : index534 // CHECK-DAG: %[[M:.+]] = tensor.dim %[[A]], %[[c0]] :535 // CHECK-DAG: %[[N:.+]] = tensor.dim %[[B]], %[[c1]] :536 // CHECK-DAG: %[[NT0:.+]] = affine.apply #map()[%[[M]]]537 // CHECK-DAG: %[[NT1:.+]] = affine.apply #map1()[%[[N]]]538 // CHECK-DAG: %[[K:.+]] = tensor.dim %[[A]], %[[c1]] :539 // CHECK: scf.forall (%[[IV0:.+]], %[[IV1:.+]]) in (%[[NT0]], %[[NT1]]) shared_outs(%[[C_BLK:.*]] = %[[C]])540 // CHECK-DAG: %[[TS0:.+]] = affine.min #[[$map2]](%[[IV0]])[%[[M]]]541 // CHECK-DAG: %[[TS1:.+]] = affine.min #[[$map3]](%[[IV1]])[%[[N]]]542 // CHECK-DAG: %[[LB0:.+]] = affine.apply #[[$map4]](%[[IV0]])543 // CHECK-DAG: %[[LB1:.+]] = affine.apply #[[$map5]](%[[IV1]])544 // CHECK: tensor.extract_slice %[[A]][%[[LB0]], 0] [%[[TS0]], %[[K]]] [1, 1] :545 // CHECK: tensor.extract_slice %[[B]][0, %[[LB1]]] [%[[K]], %[[TS1]]] [1, 1] :546 // CHECK: tensor.extract_slice %[[C_BLK]][%[[LB0]], %[[LB1]]] [%[[TS0]], %[[TS1]]] [1, 1] :547 // CHECK: linalg.matmul548 // CHECK: scf.forall.in_parallel549 // CHECK-NEXT: tensor.parallel_insert_slice550 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)551 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)552 return %0 : tensor<?x?xf32>553}554 555module attributes {transform.with_named_sequence} {556 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {557 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op558 %c10 = transform.param.constant 10 : i64 -> !transform.any_param559 %c20 = transform.param.constant 20 : i64 -> !transform.any_param560 %sz = transform.merge_handles %c10, %c20 : !transform.any_param561 %1:2 = transform.structured.tile_using_forall %0 tile_sizes *(%sz)562 : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op)563 transform.yield564 }565}566 567// -----568 569func.func @matmul_tile_size_dynamic(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {570 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)571 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)572 return %0 : tensor<?x?xf32>573}574 575module attributes {transform.with_named_sequence} {576 transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {577 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op578 %sz = transform.param.constant "[10 : i64, 20 : i64]" -> !transform.any_param579 // expected-error @below {{expected the parameter to be associated with an integer attribute}}580 %1:2 = transform.structured.tile_using_forall %0 tile_sizes *(%sz)581 : (!transform.any_op, !transform.any_param) -> (!transform.any_op, !transform.any_op)582 transform.yield583 }584}585 586// -----587 588#map = affine_map<(d0, d1) -> (d0, d1)>589#map1 = affine_map<(d0, d1) -> (d0)>590 591func.func @tile_thread_safety1(%arg0: tensor<100x300xf32>, %arg1: tensor<100xf32>) -> tensor<100xf32> {592 // expected-warning@below {{tiling is not thread safe at axis #1}}593 %0 = linalg.generic {indexing_maps = [#map, #map1], iterator_types = ["parallel", "reduction"]} ins(%arg0 : tensor<100x300xf32>) outs(%arg1 : tensor<100xf32>) {594 ^bb0(%in: f32, %out: f32):595 %1 = arith.addf %in, %out : f32596 linalg.yield %1 : f32597 } -> tensor<100xf32>598 return %0 : tensor<100xf32>599}600 601module attributes {transform.with_named_sequence} {602 transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {603 %0 = transform.structured.match ops{["linalg.generic"]} in %arg0 : (!transform.any_op) -> !transform.any_op604 %forall, %tiled_generic = transform.structured.tile_using_forall %0 num_threads [4, 2]605 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)606 transform.yield607 }608}609 610// -----611 612#map = affine_map<(d0, d1, d2) -> (d0, d1, d2)>613#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>614 615func.func @tile_thread_safety2(%arg0: tensor<100x300x8xf32>, %arg1: tensor<300x8xf32>) -> tensor<300x8xf32> {616 // expected-warning@below {{tiling is not thread safe at axis #0}}617 %0 = linalg.generic {indexing_maps = [#map, #map1], iterator_types = ["reduction", "parallel", "parallel"]} ins(%arg0 : tensor<100x300x8xf32>) outs(%arg1 : tensor<300x8xf32>) {618 ^bb0(%in: f32, %out: f32):619 %1 = arith.addf %in, %out : f32620 linalg.yield %1 : f32621 } -> tensor<300x8xf32>622 return %0 : tensor<300x8xf32>623}624 625module attributes {transform.with_named_sequence} {626 transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {627 %0 = transform.structured.match ops{["linalg.generic"]} in %arg0 : (!transform.any_op) -> !transform.any_op628 %forall, %tiled_generic = transform.structured.tile_using_forall %0 num_threads [8]629 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)630 transform.yield631 }632}633 634// -----635 636#map = affine_map<(d0, d1, d2) -> (d0, d1, d2)>637#map1 = affine_map<(d0, d1, d2) -> (d0, d2)>638 639func.func @tile_thread_safety3(%arg0: tensor<100x300x8xf32>, %arg1: tensor<100x8xf32>) -> tensor<100x8xf32> {640 // expected-warning@below {{tiling is not thread safe at axis #1}}641 %0 = linalg.generic {indexing_maps = [#map, #map1], iterator_types = ["parallel", "reduction", "parallel"]} ins(%arg0 : tensor<100x300x8xf32>) outs(%arg1 : tensor<100x8xf32>) {642 ^bb0(%in: f32, %out: f32):643 %1 = arith.addf %in, %out : f32644 linalg.yield %1 : f32645 } -> tensor<100x8xf32>646 return %0 : tensor<100x8xf32>647}648 649module attributes {transform.with_named_sequence} {650 transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {651 %0 = transform.structured.match ops{["linalg.generic"]} in %arg0 : (!transform.any_op) -> !transform.any_op652 %forall, %tiled_generic = transform.structured.tile_using_forall %0 num_threads [8, 4, 2]653 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)654 transform.yield655 }656}657 658// -----659 660#map = affine_map<(d0, d1, d2) -> (d0, d1, d2)>661#map1 = affine_map<(d0, d1, d2) -> (d0, d2)>662#map2 = affine_map<(d0, d1, d2) -> (d2)>663 664func.func @tile_thread_safety4(%arg0: tensor<100x300x8xf32>, %arg1: tensor<100x8xf32>, %arg2 : tensor<8xf32>) -> (tensor<100x8xf32>, tensor<8xf32>) {665 // expected-warning@+2 {{tiling is not thread safe at axis #0}}666 // expected-warning@below {{tiling is not thread safe at axis #1}}667 %0:2 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["reduction", "reduction", "parallel"]} ins(%arg0 : tensor<100x300x8xf32>) outs(%arg1, %arg2 : tensor<100x8xf32>, tensor<8xf32>) {668 ^bb0(%in: f32, %out1: f32, %out2: f32):669 %1 = arith.addf %in, %out1 : f32670 %2 = arith.addf %in, %out2 : f32671 linalg.yield %1, %2 : f32, f32672 } -> (tensor<100x8xf32>, tensor<8xf32>)673 return %0#0, %0#1 : tensor<100x8xf32>, tensor<8xf32>674}675 676module attributes {transform.with_named_sequence} {677 transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {678 %0 = transform.structured.match ops{["linalg.generic"]} in %arg0 : (!transform.any_op) -> !transform.any_op679 %forall, %tiled_generic = transform.structured.tile_using_forall %0 num_threads [8, 4, 2]680 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)681 transform.yield682 }683}684 685// -----686 687#map = affine_map<(d0, d1) -> (d0, d1)>688#map1 = affine_map<(d0, d1) -> (d0)>689 690func.func @tile_thread_safety5(%arg0: tensor<100x300xf32>, %arg1: tensor<100xf32>) -> tensor<100xf32> {691 // expected-warning@below {{tiling is not thread safe at axis #1}}692 %0 = linalg.generic {indexing_maps = [#map, #map1], iterator_types = ["parallel", "reduction"]} ins(%arg0 : tensor<100x300xf32>) outs(%arg1 : tensor<100xf32>) {693 ^bb0(%in: f32, %out: f32):694 %1 = arith.addf %in, %out : f32695 linalg.yield %1 : f32696 } -> tensor<100xf32>697 return %0 : tensor<100xf32>698}699 700module attributes {transform.with_named_sequence} {701 transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {702 %0 = transform.structured.match ops{["linalg.generic"]} in %arg0 : (!transform.any_op) -> !transform.any_op703 %forall, %tiled_generic = transform.structured.tile_using_forall %0 tile_sizes [10, 1]704 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)705 transform.yield706 }707}708 709// -----710 711func.func @tile_thread_safety6(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, %C: tensor<?x?xf32>) -> tensor<?x?xf32> {712 // expected-warning@below {{tiling is not thread safe at axis #2}}713 %0 = linalg.matmul ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>)714 outs(%C : tensor<?x?xf32>) -> (tensor<?x?xf32>)715 return %0 : tensor<?x?xf32>716}717 718module attributes {transform.with_named_sequence} {719 transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {720 %0 = transform.structured.match ops{["linalg.matmul"]} in %arg0 : (!transform.any_op) -> !transform.any_op721 %forall, %tiled_generic = transform.structured.tile_using_forall %0 num_threads [2, 0, 8]722 : (!transform.any_op) -> (!transform.any_op, !transform.any_op)723 transform.yield724 }725}726