File: fold-into-pack-and-unpack.mlir

package info (click to toggle)
llvm-toolchain-18 1%3A18.1.8-18
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,908,340 kB
  • sloc: cpp: 6,667,937; ansic: 1,440,452; asm: 883,619; python: 230,549; objc: 76,880; f90: 74,238; lisp: 35,989; pascal: 16,571; sh: 10,229; perl: 7,459; ml: 5,047; awk: 3,523; makefile: 2,987; javascript: 2,149; xml: 892; fortran: 649; cs: 573
file content (524 lines) | stat: -rw-r--r-- 25,073 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
// RUN: mlir-opt -split-input-file -test-tensor-transform-patterns=test-fold-into-pack-and-unpack  %s | FileCheck %s

func.func @fold_unpack_slice(%arg0 : tensor<?x?x8x4xf32>, %arg1 : tensor<?x?xf32>,
    %arg2 : index, %arg3 : index) -> tensor<?x?xf32> {
  %0 = tensor.unpack %arg0 inner_dims_pos = [0, 1] inner_tiles = [8, 4] into %arg1
      : tensor<?x?x8x4xf32> -> tensor<?x?xf32>
  %1 = tensor.extract_slice %0[0, 0] [%arg2, %arg3] [1, 1] : tensor<?x?xf32> to tensor<?x?xf32>
  return %1 : tensor<?x?xf32>
}
//      CHECK: func @fold_unpack_slice(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<?x?x8x4xf32>
// CHECK-SAME:     %[[ARG1:[a-zA-Z0-9]+]]: tensor<?x?xf32>
// CHECK-SAME:     %[[ARG2:[a-zA-Z0-9]+]]: index
// CHECK-SAME:     %[[ARG3:[a-zA-Z0-9]+]]: index
//      CHECK:   %[[INIT:.+]] = tensor.empty(%[[ARG2]], %[[ARG3]]) : tensor<?x?xf32>
//      CHECK:   %[[UNPACK:.+]] = tensor.unpack %[[ARG0]] inner_dims_pos = [0, 1] inner_tiles = [8, 4]
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[UNPACK]]

// -----

func.func @nofold_unpack_slice_non_zero_offset(%arg0 : tensor<?x?x8x4xf32>, %arg1 : tensor<?x?xf32>,
    %arg2 : index, %arg3 : index, %arg4 : index) -> tensor<?x?xf32> {
  %0 = tensor.unpack %arg0 inner_dims_pos = [0, 1] inner_tiles = [8, 4] into %arg1
      : tensor<?x?x8x4xf32> -> tensor<?x?xf32>
  %1 = tensor.extract_slice %0[0, %arg4] [%arg2, %arg3] [1, 1] : tensor<?x?xf32> to tensor<?x?xf32>
  return %1 : tensor<?x?xf32>
}
// CHECK-LABEL: func @nofold_unpack_slice_non_zero_offset(
//       CHECK:   %[[UNPACK:.+]] = tensor.unpack
//       CHECK:   tensor.extract_slice %[[UNPACK]]

// -----

func.func @nofold_unpack_slice_non_unit_stride(%arg0 : tensor<?x?x8x4xf32>, %arg1 : tensor<?x?xf32>,
    %arg2 : index, %arg3 : index, %arg4 : index) -> tensor<?x?xf32> {
  %0 = tensor.unpack %arg0 inner_dims_pos = [0, 1] inner_tiles = [8, 4] into %arg1
      : tensor<?x?x8x4xf32> -> tensor<?x?xf32>
  %1 = tensor.extract_slice %0[0, 0] [%arg2, %arg3] [%arg4, 1] : tensor<?x?xf32> to tensor<?x?xf32>
  return %1 : tensor<?x?xf32>
}
// CHECK-LABEL: func @nofold_unpack_slice_non_unit_stride(
//       CHECK:   %[[UNPACK:.+]] = tensor.unpack
//       CHECK:   tensor.extract_slice %[[UNPACK]]

// -----

func.func @nofold_unpack_slice_rank_reduced(%arg0 : tensor<?x?x8x4xf32>, %arg1 : tensor<?x?xf32>,
    %arg2 : index, %arg3 : index) -> tensor<f32> {
  %0 = tensor.unpack %arg0 inner_dims_pos = [0, 1] inner_tiles = [8, 4] into %arg1
      : tensor<?x?x8x4xf32> -> tensor<?x?xf32>
  %1 = tensor.extract_slice %0[0, 0] [1, 1] [1, 1] : tensor<?x?xf32> to tensor<f32>
  return %1 : tensor<f32>
}
// CHECK-LABEL: func @nofold_unpack_slice_rank_reduced(
//       CHECK:   %[[UNPACK:.+]] = tensor.unpack
//       CHECK:   tensor.extract_slice %[[UNPACK]]

// -----

func.func @pad_pack(%src: tensor<16641x16xf32>) -> tensor<2082x1x8x32xf32> {
  %c0 = arith.constant 0 : index
  %cst = arith.constant 0.000000e+00 : f32
  %padded = tensor.pad %src low[0, 0] high[15, 0] {
  ^bb0(%arg0: index, %arg1: index):
    tensor.yield %cst : f32
  } : tensor<16641x16xf32> to tensor<16656x16xf32>
  %empty = tensor.empty() : tensor<2082x1x8x32xf32>
  %pack = tensor.pack %padded padding_value(%cst : f32) inner_dims_pos = [0, 1] inner_tiles = [8, 32] into %empty
      : tensor<16656x16xf32> -> tensor<2082x1x8x32xf32>
  return %pack : tensor<2082x1x8x32xf32>
}
// CHECK-LABEL: func.func @pad_pack
// CHECK-SAME:    %[[SRC:[a-zA-Z0-9]+]]
// CHECK:         %[[PAD_VAL:.+]] = arith.constant 0.000000e+00 : f32
// CHECK:         %[[DEST:.+]] = tensor.empty() : tensor<2082x1x8x32xf32>
// CHECK:         %[[PACK:.+]] = tensor.pack %[[SRC]]
// CHECK-SAME:      padding_value(%[[PAD_VAL]] : f32)
// CHECK-SAME:      inner_dims_pos = [0, 1] inner_tiles = [8, 32] into %[[DEST]]

// -----

func.func @nofold_pad_pack(%src: tensor<16641x16xf32>) -> tensor<2082x1x8x32xf32> {
  %c0 = arith.constant 0 : index
  %cst = arith.constant 0.000000e+00 : f32
  %padded = tensor.pad %src nofold low[0, 0] high[15, 0] {
  ^bb0(%arg0: index, %arg1: index):
    tensor.yield %cst : f32
  } : tensor<16641x16xf32> to tensor<16656x16xf32>
  %empty = tensor.empty() : tensor<2082x1x8x32xf32>
  %pack = tensor.pack %padded padding_value(%cst : f32) inner_dims_pos = [0, 1] inner_tiles = [8, 32] into %empty
      : tensor<16656x16xf32> -> tensor<2082x1x8x32xf32>
  return %pack : tensor<2082x1x8x32xf32>
}
// CHECK-LABEL: func.func @nofold_pad_pack
// CHECK:         tensor.pad
// CHECK:         tensor.pack

// -----

func.func @pad_pack_different_padding_value(%src: tensor<16641x16xf32>) -> tensor<2082x1x8x32xf32> {
  %c0 = arith.constant 0 : index
  %cst0 = arith.constant 0.000000e+00 : f32
  %cst1 = arith.constant 1.000000e+00 : f32
  %padded = tensor.pad %src low[0, 0] high[15, 0] {
  ^bb0(%arg0: index, %arg1: index):
    tensor.yield %cst0 : f32
  } : tensor<16641x16xf32> to tensor<16656x16xf32>
  %empty = tensor.empty() : tensor<2082x1x8x32xf32>
  %pack = tensor.pack %padded padding_value(%cst1 : f32) inner_dims_pos = [0, 1] inner_tiles = [8, 32] into %empty
      : tensor<16656x16xf32> -> tensor<2082x1x8x32xf32>
  return %pack : tensor<2082x1x8x32xf32>
}
// CHECK-LABEL: func.func @pad_pack_different_padding_value
// CHECK:         tensor.pad
// CHECK:         tensor.pack

// -----

func.func @tensor_pack_linalg_transpose_fold(%arg0: tensor<56x57x1x64xf32>) -> tensor<1x57x56x2x32xf32> {
  %0 = tensor.empty() : tensor<56x2x1x57x32xf32>
  %pack = tensor.pack %arg0
    outer_dims_perm = [0, 3, 2, 1]
    inner_dims_pos = [3]
    inner_tiles = [32]
    into %0 : tensor<56x57x1x64xf32> -> tensor<56x2x1x57x32xf32>

  %1 = tensor.empty() : tensor<1x57x56x2x32xf32>
  %transposed = linalg.transpose
    ins(%pack : tensor<56x2x1x57x32xf32>)
    outs(%1 : tensor<1x57x56x2x32xf32>)
    permutation = [2, 3, 0, 1, 4]
  return %transposed : tensor<1x57x56x2x32xf32>
}
//      CHECK: func @tensor_pack_linalg_transpose_fold(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x57x1x64xf32>)
//      CHECK:   %[[INIT:.+]] = tensor.empty() : tensor<1x57x56x2x32xf32>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]]
// CHECK-SAME:      outer_dims_perm = [2, 1, 0, 3]
// CHECK-SAME:      inner_dims_pos = [3] inner_tiles = [32] 
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[PACK]]

// -----

func.func @tensor_pack_linalg_transpose_fold_with_padding(%arg0: tensor<56x57x1x55xf32>, %padding: f32) -> tensor<1x57x56x2x32xf32> {
  %0 = tensor.empty() : tensor<56x2x1x57x32xf32>
  %pack = tensor.pack %arg0 padding_value(%padding : f32)
    outer_dims_perm = [0, 3, 2, 1]
    inner_dims_pos = [3]
    inner_tiles = [32]
    into %0 : tensor<56x57x1x55xf32> -> tensor<56x2x1x57x32xf32>

  %1 = tensor.empty() : tensor<1x57x56x2x32xf32>
  %transposed = linalg.transpose
    ins(%pack : tensor<56x2x1x57x32xf32>)
    outs(%1 : tensor<1x57x56x2x32xf32>)
    permutation = [2, 3, 0, 1, 4]
  return %transposed : tensor<1x57x56x2x32xf32>
}
//      CHECK: func @tensor_pack_linalg_transpose_fold_with_padding(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x57x1x55xf32>, %[[PADDING:.+]]: f32)
//      CHECK:   %[[INIT:.+]] = tensor.empty() : tensor<1x57x56x2x32xf32>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]] padding_value(%[[PADDING]] : f32)
// CHECK-SAME:      outer_dims_perm = [2, 1, 0, 3]
// CHECK-SAME:      inner_dims_pos = [3] inner_tiles = [32] 
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[PACK]]

// -----

func.func @tensor_pack_linalg_transpose_fold_no_outer_dims_perm(%arg0: tensor<56x57x1x64xf32>) -> tensor<1x2x56x57x32xf32> {
  %0 = tensor.empty() : tensor<56x57x1x2x32xf32>
  %pack = tensor.pack %arg0
    inner_dims_pos = [3]
    inner_tiles = [32]
    into %0 : tensor<56x57x1x64xf32> -> tensor<56x57x1x2x32xf32>

  %1 = tensor.empty() : tensor<1x2x56x57x32xf32>
  %transposed = linalg.transpose
    ins(%pack : tensor<56x57x1x2x32xf32>)
    outs(%1 : tensor<1x2x56x57x32xf32>)
    permutation = [2, 3, 0, 1, 4]
  return %transposed : tensor<1x2x56x57x32xf32>
}
//      CHECK: func @tensor_pack_linalg_transpose_fold_no_outer_dims_perm(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x57x1x64xf32>)
//      CHECK:   %[[INIT:.+]] = tensor.empty() : tensor<1x2x56x57x32xf32>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]]
// CHECK-SAME:      outer_dims_perm = [2, 3, 0, 1]
// CHECK-SAME:      inner_dims_pos = [3] inner_tiles = [32] 
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[PACK]]

// -----

func.func @tensor_pack_linalg_transpose_fold_tile_dims_transpose(%arg0: tensor<56x72x24x128xf32>) -> tensor<12x56x4x9x32x8x2xf32> {
  %0 = tensor.empty() : tensor<4x9x12x56x8x2x32xf32>
  %pack = tensor.pack %arg0
    outer_dims_perm = [3, 1, 2, 0]
    inner_dims_pos = [1, 2, 3]
    inner_tiles = [8, 2, 32]
    into %0 : tensor<56x72x24x128xf32> -> tensor<4x9x12x56x8x2x32xf32>

  %1 = tensor.empty() : tensor<12x56x4x9x32x8x2xf32>
  %transposed = linalg.transpose
    ins(%pack : tensor<4x9x12x56x8x2x32xf32>)
    outs(%1 : tensor<12x56x4x9x32x8x2xf32>)
    permutation = [2, 3, 0, 1, 6, 4, 5]
  return %transposed : tensor<12x56x4x9x32x8x2xf32>
}
//      CHECK: func @tensor_pack_linalg_transpose_fold_tile_dims_transpose(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x72x24x128xf32>)
//      CHECK:   %[[INIT:.+]] = tensor.empty() : tensor<12x56x4x9x32x8x2xf32>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]]
// CHECK-SAME:      outer_dims_perm = [2, 0, 3, 1]
// CHECK-SAME:      inner_dims_pos = [3, 1, 2] inner_tiles = [32, 8, 2] 
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[PACK]]

// -----

func.func @tensor_pack_linalg_transpose_fold_tile_dims_outer_dims_transpose(%arg0: tensor<56x72x24x128xf32>) -> tensor<9x56x2x12x32x8x4xf32> {
  %0 = tensor.empty() : tensor<4x12x9x56x8x2x32xf32>
  %pack = tensor.pack %arg0
    outer_dims_perm = [3, 2, 1, 0]
    inner_dims_pos = [1, 2, 3]
    inner_tiles = [8, 2, 32]
    into %0 : tensor<56x72x24x128xf32> -> tensor<4x12x9x56x8x2x32xf32>

  %1 = tensor.empty() : tensor<9x56x2x12x32x8x4xf32>
  %transposed = linalg.transpose
    ins(%pack : tensor<4x12x9x56x8x2x32xf32>)
    outs(%1 : tensor<9x56x2x12x32x8x4xf32>)
    permutation = [2, 3, 5, 1, 6, 4, 0]
  return %transposed : tensor<9x56x2x12x32x8x4xf32>
}
//      CHECK: func @tensor_pack_linalg_transpose_fold_tile_dims_outer_dims_transpose(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x72x24x128xf32>)
//      CHECK:   tensor.pack
//      CHECK:   linalg.transpose

// -----

func.func @tensor_pack_linalg_transpose_fold_dynamic_outer_dims(%arg0: tensor<56x?x?x64xf32>) -> tensor<?x?x56x2x32xf32> {
  %0 = tensor.empty() : tensor<56x2x1x57x32xf32>
  %pack = tensor.pack %arg0
    outer_dims_perm = [0, 3, 2, 1]
    inner_dims_pos = [3]
    inner_tiles = [32]
    into %0 : tensor<56x?x?x64xf32> -> tensor<56x2x1x57x32xf32>

  %1 = tensor.empty() : tensor<1x57x56x2x32xf32>
  %transposed = linalg.transpose
    ins(%pack : tensor<56x2x1x57x32xf32>)
    outs(%1 : tensor<1x57x56x2x32xf32>)
    permutation = [2, 3, 0, 1, 4]

  %return_value = tensor.cast %transposed : tensor<1x57x56x2x32xf32> to tensor<?x?x56x2x32xf32>  
  return %return_value : tensor<?x?x56x2x32xf32>
}
//      CHECK: func @tensor_pack_linalg_transpose_fold_dynamic_outer_dims(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x?x?x64xf32>)
//  CHECK-DAG:   %[[c1:.+]] = arith.constant 1 : index
//  CHECK-DAG:   %[[c2:.+]] = arith.constant 2 : index
//      CHECK:   %[[dim:.+]] = tensor.dim %[[ARG0]], %[[c1]] : tensor<56x?x?x64xf32>
//      CHECK:   %[[dim_0:.+]] = tensor.dim %[[ARG0]], %[[c2]] : tensor<56x?x?x64xf32>
//      CHECK:   %[[INIT:.+]] = tensor.empty(%[[dim_0]], %[[dim]]) : tensor<?x?x56x2x32xf32>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]]
// CHECK-SAME:      outer_dims_perm = [2, 1, 0, 3]
// CHECK-SAME:      inner_dims_pos = [3] inner_tiles = [32] 
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[PACK]]

// -----

func.func @tensor_pack_linalg_transpose_fold_dynamic_outer_and_tile_dims(%arg0: tensor<56x?x?x128xf32>) -> tensor<?x?x56x9x32x8x2xf32> {
  %0 = tensor.empty() : tensor<56x9x12x4x8x2x32xf32>
  %pack = tensor.pack %arg0
    inner_dims_pos = [1, 2, 3]
    inner_tiles = [8, 2, 32]
    into %0 : tensor<56x?x?x128xf32> -> tensor<56x9x12x4x8x2x32xf32>

  %1 = tensor.empty() : tensor<12x4x56x9x32x8x2xf32>
  %transposed = linalg.transpose
    ins(%pack : tensor<56x9x12x4x8x2x32xf32>)
    outs(%1 : tensor<12x4x56x9x32x8x2xf32>)
    permutation = [2, 3, 0, 1, 6, 4, 5]
  
  %return_value = tensor.cast %transposed : tensor<12x4x56x9x32x8x2xf32> to tensor<?x?x56x9x32x8x2xf32> 
  return %return_value : tensor<?x?x56x9x32x8x2xf32>
}
//      CHECK: #[[map:.+]] = affine_map<()[s0] -> (s0 ceildiv 8)>
//      CHECK: #[[map1:.+]] = affine_map<()[s0] -> (s0 ceildiv 2)>
//      CHECK: module {
//      CHECK:   func.func @tensor_pack_linalg_transpose_fold_dynamic_outer_and_tile_dims(
// CHECK-SAME:   %[[ARG0:.+]]: tensor<56x?x?x128xf32>) 
//  CHECK-DAG:     %[[c1:.+]] = arith.constant 1 : index
//  CHECK-DAG:     %[[c2:.+]] = arith.constant 2 : index
//      CHECK:     %[[dim:.+]] = tensor.dim %[[ARG0]], %[[c1]] : tensor<56x?x?x128xf32>
//      CHECK:     %[[dim_0:.+]] = tensor.dim %[[ARG0]], %[[c2]] : tensor<56x?x?x128xf32>
//      CHECK:     %[[mapped_dim1:.+]] = affine.apply #[[map:.+]]()[%[[dim]]]
//      CHECK:     %[[mapped_dim2:.+]] = affine.apply #[[map1:.+]]()[%[[dim_0]]]
//      CHECK:     %[[INIT:.+]] = tensor.empty(%[[mapped_dim2]], %[[mapped_dim1]]) : tensor<?x4x56x?x32x8x2xf32>
//      CHECK:     %[[PACK:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [2, 3, 0, 1] inner_dims_pos = [3, 1, 2] inner_tiles = [32, 8, 2] into %[[INIT]] : tensor<56x?x?x128xf32> -> tensor<?x4x56x?x32x8x2xf32>
//      CHECK:     %[[CAST:.+]] = tensor.cast %[[PACK]] : tensor<?x4x56x?x32x8x2xf32> to tensor<?x?x56x9x32x8x2xf32>
//      CHECK:     return %[[CAST]] : tensor<?x?x56x9x32x8x2xf32>
//      CHECK:   }

// -----

func.func @tensor_pack_linalg_transpose_fold_dynamic_outer_dims_tile_dims_tile_sizes(%arg0: tensor<?x?x?x?xf32>, %pack_dest: tensor<?x?x?x?x?x?x?xf32>, %transpose_dest: tensor<?x?x?x?x?x?x?xf32>, %tile_p : index, %tile_q : index, %tile_r : index) -> tensor<?x?x?x?x?x?x?xf32> {
  %pack = tensor.pack %arg0
    outer_dims_perm = [3, 0, 2, 1]
    inner_dims_pos = [1, 2, 3]
    inner_tiles = [%tile_p, %tile_q, %tile_r]
    into %pack_dest : tensor<?x?x?x?xf32> -> tensor<?x?x?x?x?x?x?xf32>

  %transposed = linalg.transpose
    ins(%pack : tensor<?x?x?x?x?x?x?xf32>)
    outs(%transpose_dest : tensor<?x?x?x?x?x?x?xf32>)
    permutation = [2, 3, 0, 1, 6, 4, 5]

  return %transposed : tensor<?x?x?x?x?x?x?xf32>
}
//      CHECK: #[[map:.+]] = affine_map<()[s0, s1] -> (s0 ceildiv s1)>
//      CHECK: module {
//      CHECK:   func.func @tensor_pack_linalg_transpose_fold_dynamic_outer_dims_tile_dims_tile_sizes(
// CHECK-SAME:   %[[ARG0:.+]]: tensor<?x?x?x?xf32>,
// CHECK-SAME:   %[[PACK_DEST:.+]]: tensor<?x?x?x?x?x?x?xf32>, %[[TRANSPOSE_DEST:.+]]: tensor<?x?x?x?x?x?x?xf32>,
// CHECK-SAME:   %[[ARG1:.+]]: index, %[[ARG2:.+]]: index,
// CHECK-SAME:   %[[ARG3:.+]]: index) 
//  CHECK-DAG:     %[[c0:.+]] = arith.constant 0 : index
//  CHECK-DAG:     %[[c1:.+]] = arith.constant 1 : index
//  CHECK-DAG:     %[[c2:.+]] = arith.constant 2 : index
//  CHECK-DAG:     %[[c3:.+]] = arith.constant 3 : index
//      CHECK:     %[[dim:.+]] = tensor.dim %[[ARG0]], %[[c0]] : tensor<?x?x?x?xf32>
//      CHECK:     %[[dim_0:.+]] = tensor.dim %[[ARG0]], %[[c1]] : tensor<?x?x?x?xf32>
//      CHECK:     %[[dim_1:.+]] = tensor.dim %[[ARG0]], %[[c2]] : tensor<?x?x?x?xf32>
//      CHECK:     %[[dim_2:.+]] = tensor.dim %[[ARG0]], %[[c3]] : tensor<?x?x?x?xf32>
//      CHECK:     %[[mapped_dim0:.+]] = affine.apply #[[map:.+]]()[%[[dim_2]], %[[ARG3]]]
//      CHECK:     %[[mapped_dim1:.+]] = affine.apply #[[map:.+]]()[%[[dim_0]], %[[ARG1]]]
//      CHECK:     %[[mapped_dim2:.+]] = affine.apply #[[map:.+]]()[%[[dim_1]], %[[ARG2]]]
//      CHECK:     %[[INIT:.+]] = tensor.empty(%[[mapped_dim2]], %[[mapped_dim1]], %[[mapped_dim0]], %[[dim]], %[[ARG3]], %[[ARG1]], %[[ARG2]]) : tensor<?x?x?x?x?x?x?xf32>
//      CHECK:     %[[PACK:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [2, 1, 3, 0] inner_dims_pos = [3, 1, 2] inner_tiles = [%[[ARG3]], %[[ARG1]], %[[ARG2]]] into %[[INIT]] : tensor<?x?x?x?xf32> -> tensor<?x?x?x?x?x?x?xf32>
//      CHECK:     return %[[PACK]] : tensor<?x?x?x?x?x?x?xf32>
//      CHECK:   }

// -----

func.func @linalg_transpose_tensor_pack_fold(%arg0: tensor<56x57x1x64xf32>) -> tensor<1x57x56x2x32xf32> {
  %0 = tensor.empty() : tensor<1x56x57x64xf32>
  %transposed = linalg.transpose
    ins(%arg0 : tensor<56x57x1x64xf32>)
    outs(%0 : tensor<1x56x57x64xf32>)
    permutation = [2, 0, 1, 3]

  %1 = tensor.empty() : tensor<1x57x56x2x32xf32>
  %pack = tensor.pack %transposed
    outer_dims_perm = [0, 2, 1, 3]
    inner_dims_pos = [3]
    inner_tiles = [32]
    into %1 : tensor<1x56x57x64xf32> -> tensor<1x57x56x2x32xf32>
  return %pack : tensor<1x57x56x2x32xf32>
}
//CHECK-LABEL: func @linalg_transpose_tensor_pack_fold(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x57x1x64xf32>)
//      CHECK:   %[[INIT:.+]] = tensor.empty() : tensor<1x57x56x2x32xf32>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]]
// CHECK-SAME:      outer_dims_perm = [2, 1, 0, 3]
// CHECK-SAME:      inner_dims_pos = [3] inner_tiles = [32] 
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[PACK]]

// -----

func.func @linalg_transpose_tensor_pack_fold_with_padding(%arg0: tensor<56x57x1x55xf32>, %padding: f32) -> tensor<1x57x56x2x32xf32> {
  %0 = tensor.empty() : tensor<1x56x57x55xf32>
  %transpose = linalg.transpose
    ins(%arg0 : tensor<56x57x1x55xf32>)
    outs(%0 : tensor<1x56x57x55xf32>)
    permutation = [2, 0, 1, 3]
  
  %1 = tensor.empty() : tensor<1x57x56x2x32xf32>
  %pack = tensor.pack %transpose padding_value(%padding : f32)
    outer_dims_perm = [0, 2, 1, 3]
    inner_dims_pos = [3]
    inner_tiles = [32]
    into %1 : tensor<1x56x57x55xf32> -> tensor<1x57x56x2x32xf32>
  return %pack : tensor<1x57x56x2x32xf32>
}
//CHECK-LABEL: func @linalg_transpose_tensor_pack_fold_with_padding(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x57x1x55xf32>, %[[PADDING:.+]]: f32)
//      CHECK:   %[[INIT:.+]] = tensor.empty() : tensor<1x57x56x2x32xf32>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]] padding_value(%[[PADDING]] : f32)
// CHECK-SAME:      outer_dims_perm = [2, 1, 0, 3]
// CHECK-SAME:      inner_dims_pos = [3] inner_tiles = [32] 
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[PACK]]

// -----

func.func @linalg_transpose_tensor_pack_fold_no_outer_dims_perm(%arg0: tensor<56x57x1x64xf32>) -> tensor<1x56x57x2x32xf32> {
  %0 = tensor.empty() : tensor<1x56x57x64xf32>
  %transposed = linalg.transpose
    ins(%arg0 : tensor<56x57x1x64xf32>)
    outs(%0 : tensor<1x56x57x64xf32>)
    permutation = [2, 0, 1, 3]
  
  %1 = tensor.empty() : tensor<1x56x57x2x32xf32>
  %pack = tensor.pack %transposed
    inner_dims_pos = [3]
    inner_tiles = [32]
    into %1 : tensor<1x56x57x64xf32> -> tensor<1x56x57x2x32xf32>
  return %pack : tensor<1x56x57x2x32xf32>
}
//CHECK-LABEL: func @linalg_transpose_tensor_pack_fold_no_outer_dims_perm(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<56x57x1x64xf32>)
//      CHECK:   %[[INIT:.+]] = tensor.empty() : tensor<1x56x57x2x32xf32>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]]
// CHECK-SAME:      outer_dims_perm = [2, 0, 1, 3]
// CHECK-SAME:      inner_dims_pos = [3] inner_tiles = [32] 
// CHECK-SAME:       into %[[INIT]]
//      CHECK:   return %[[PACK]]

// -----

func.func @linalg_transpose_tensor_pack_fold_complex_inner_dims_change(%arg0: tensor<25x30x35x40xf32>, %transpose_dest: tensor<35x40x25x30xf32>, %pack_dest: tensor<3x35x5x8x5x10x5xf32>) -> tensor<3x35x5x8x5x10x5xf32> {
  %transposed = linalg.transpose
    ins(%arg0 : tensor<25x30x35x40xf32>)
    outs(%transpose_dest : tensor<35x40x25x30xf32>)
    permutation = [2, 3, 0, 1]
  
  %pack = tensor.pack %transposed
    outer_dims_perm = [3, 0, 2, 1]
    inner_dims_pos = [1, 3, 2]
    inner_tiles = [5, 10, 5]
    into %pack_dest : tensor<35x40x25x30xf32> -> tensor<3x35x5x8x5x10x5xf32>
  return %pack : tensor<3x35x5x8x5x10x5xf32>
}
//CHECK-LABEL:   func.func @linalg_transpose_tensor_pack_fold_complex_inner_dims_change(
// CHECK-SAME:     %[[ARG0:.+]]: tensor<25x30x35x40xf32>, 
// CHECK-SAME:     %[[ARG1:.+]]: tensor<35x40x25x30xf32>, 
// CHECK-SAME:     %[[ARG2:.+]]: tensor<3x35x5x8x5x10x5xf32>) -> tensor<3x35x5x8x5x10x5xf32> {
//      CHECK:     %[[VAL0:.+]] = tensor.empty() : tensor<3x35x5x8x5x10x5xf32>
//      CHECK:     %[[PACK:.+]] = tensor.pack %[[ARG0]] 
// CHECK-SAME:        outer_dims_perm = [1, 2, 0, 3] 
// CHECK-SAME:        inner_dims_pos = [3, 1, 0] 
// CHECK-SAME:        inner_tiles = [5, 10, 5] 
// CHECK-SAME:         into %[[VAL0]] 
//      CHECK:     return %[[PACK]]

// -----

func.func @linalg_transpose_tensor_pack_fold_dynamic_outer_dims_tile_dims_tile_sizes(%arg0: tensor<?x?x?x?xf32>, %transpose_dest: tensor<?x?x?x?xf32>, %pack_dest: tensor<?x?x?x?x?x?x?xf32>, %tile_p : index, %tile_q : index, %tile_r : index) -> tensor<?x?x?x?x?x?x?xf32> {
  %transposed = linalg.transpose
    ins(%arg0 : tensor<?x?x?x?xf32>)
    outs(%transpose_dest : tensor<?x?x?x?xf32>)
    permutation = [2, 3, 0, 1]
  
  %pack = tensor.pack %transposed
    outer_dims_perm = [3, 0, 2, 1]
    inner_dims_pos = [1, 3, 2]
    inner_tiles = [%tile_p, %tile_q, %tile_r]
    into %pack_dest : tensor<?x?x?x?xf32> -> tensor<?x?x?x?x?x?x?xf32>
  return %pack : tensor<?x?x?x?x?x?x?xf32>
}
//      CHECK:   #[[map:.+]] = affine_map<()[s0, s1] -> (s0 ceildiv s1)>
//CHECK-LABEL:   func.func @linalg_transpose_tensor_pack_fold_dynamic_outer_dims_tile_dims_tile_sizes(
// CHECK-SAME:   %[[ARG0:.+]]: tensor<?x?x?x?xf32>, %[[ARG1:.+]]: tensor<?x?x?x?xf32>, 
// CHECK-SAME:   %[[ARG2:.+]]: tensor<?x?x?x?x?x?x?xf32>, %[[ARG3:.+]]: index, %[[ARG4:.+]]: index, %[[ARG5:.+]]: index) -> tensor<?x?x?x?x?x?x?xf32> {
//      CHECK-DAG:     %[[C0:.+]] = arith.constant 0 : index
//      CHECK-DAG:     %[[C1:.+]] = arith.constant 1 : index
//      CHECK-DAG:     %[[C2:.+]] = arith.constant 2 : index
//      CHECK-DAG:     %[[C3:.+]] = arith.constant 3 : index
//      CHECK:     %[[DIM:.+]] = tensor.dim %[[ARG0]], %[[C0]] : tensor<?x?x?x?xf32>
//      CHECK:     %[[DIM0:.+]] = tensor.dim %[[ARG0]], %[[C1]] : tensor<?x?x?x?xf32>
//      CHECK:     %[[DIM1:.+]] = tensor.dim %[[ARG0]], %[[C2]] : tensor<?x?x?x?xf32>
//      CHECK:     %[[DIM2:.+]] = tensor.dim %[[ARG0]], %[[C3]] : tensor<?x?x?x?xf32>
//      CHECK:     %[[VAL0:.+]] = affine.apply #[[map:.+]]()[%[[DIM2]], %[[ARG3]]]
//      CHECK:     %[[VAL1:.+]] = affine.apply #[[map:.+]]()[%[[DIM0]], %[[ARG4]]]
//      CHECK:     %[[VAL2:.+]] = affine.apply #[[map:.+]]()[%[[DIM]], %[[ARG5]]]
//      CHECK:     %[[VAL3:.+]] = tensor.empty(%[[VAL1]], %[[DIM1]], %[[VAL2]], %[[VAL0]], %[[ARG3]], %[[ARG4]], %[[ARG5]]) : tensor<?x?x?x?x?x?x?xf32>
//      CHECK:     %[[PACK:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [1, 2, 0, 3] inner_dims_pos = [3, 1, 0] inner_tiles = [%[[ARG3]], %[[ARG4]], %[[ARG5]]] into %[[VAL3]] : tensor<?x?x?x?xf32> -> tensor<?x?x?x?x?x?x?xf32>
//      CHECK:     return %[[PACK]] : tensor<?x?x?x?x?x?x?xf32>

// -----

func.func @linalg_transpose_tensor_pack_multiple_tiles(%arg0: tensor<?x32x128xbf16>) -> tensor<32x?x64x16x2xbf16> {
  %c0 = arith.constant 0 : index
  %cst = arith.constant 0.000000e+00 : bf16
  %dim = tensor.dim %arg0, %c0 : tensor<?x32x128xbf16>

  %0 = tensor.empty(%dim) : tensor<32x128x?xbf16>
  %transposed = linalg.transpose 
    ins(%arg0 : tensor<?x32x128xbf16>) 
    outs(%0 : tensor<32x128x?xbf16>) 
    permutation = [1, 2, 0]

  %2 = tensor.empty(%dim) : tensor<32x?x64x16x2xbf16>
  %pack = tensor.pack %transposed 
    padding_value(%cst : bf16) 
    outer_dims_perm = [0, 2, 1] 
    inner_dims_pos = [2, 1] 
    inner_tiles = [16, 2] 
    into %2 : tensor<32x128x?xbf16> -> tensor<32x?x64x16x2xbf16>
  return %pack : tensor<32x?x64x16x2xbf16>
}
//      CHECK:   #[[map:.+]] = affine_map<()[s0] -> (s0 ceildiv 16)>
//CHECK-LABEL:   func.func @linalg_transpose_tensor_pack_multiple_tiles(
// CHECK-SAME:    %[[ARG0:.+]]: tensor<?x32x128xbf16>) -> tensor<32x?x64x16x2xbf16> {
//      CHECK-DAG:   %[[C0:.+]] = arith.constant 0 : index
//      CHECK-DAG:   %[[CST:.+]] = arith.constant 0.000000e+00 : bf16
//      CHECK:   %[[DIM:.+]] = tensor.dim %[[ARG0]], %[[C0]] : tensor<?x32x128xbf16>
//      CHECK:   %[[VAL0:.+]] = affine.apply #[[map:.+]]()[%[[DIM]]]
//      CHECK:   %[[VAL1:.+]] = tensor.empty(%[[VAL0]]) : tensor<32x?x64x16x2xbf16>
//      CHECK:   %[[PACK:.+]] = tensor.pack %[[ARG0]] 
// CHECK-SAME:      padding_value(%[[CST]] : bf16) 
// CHECK-SAME:      outer_dims_perm = [1, 0, 2] 
// CHECK-SAME:      inner_dims_pos = [0, 2] 
// CHECK-SAME:      inner_tiles = [16, 2] 
// CHECK-SAME:      into %[[VAL1]] : tensor<?x32x128xbf16> -> tensor<32x?x64x16x2xbf16>
//      CHECK:   return %[[PACK]] : tensor<32x?x64x16x2xbf16>
//      CHECK:  }