Xenova HF Staff commited on
Commit
0592a26
·
verified ·
1 Parent(s): 8148465

sync 6fdf6301e2bb

Browse files
README.md CHANGED
@@ -40,12 +40,13 @@ Attributes and default values (overridable per request):
40
 
41
  | Variable | Allowed dtypes |
42
  | --- | --- |
43
- | `T` | `float32`, `float16`, `int32`, `int16`, `uint32`, `uint8`, `int8`, `bool` |
44
 
45
  ## Implementation variants
46
 
47
  One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
48
 
 
49
  - `tiled_scalar` — Stages ragged two-dimensional transpose planes through a padded workgroup tile so reads and writes remain coalesced when vec4 alignment is unavailable.
50
  - `inner_vec4` — Vectorizes the contiguous innermost dimension for permutations that leave that axis in place. It assigns multiple vectors per invocation only when enough invocations remain to keep the dispatch populated.
51
 
@@ -54,7 +55,7 @@ One implementation is selected per call from the device capabilities, the reques
54
  - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
55
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
56
  - [`test.json`](build/webgpu/test.json) — correctness cases
57
- - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
58
  - [`datamove-flat-copy.wgsl.jinja`](build/webgpu/datamove-flat-copy.wgsl.jinja)
59
  - [`datamove-transpose-2d-tiled-scalar.wgsl.jinja`](build/webgpu/datamove-transpose-2d-tiled-scalar.wgsl.jinja)
60
  - [`datamove-transpose-2d-tiled.wgsl.jinja`](build/webgpu/datamove-transpose-2d-tiled.wgsl.jinja)
@@ -64,7 +65,7 @@ One implementation is selected per call from the device capabilities, the reques
64
  ## Use with `@huggingface/kernels`
65
 
66
  ```sh
67
- npm install --save-exact @huggingface/kernels@0.0.1-preview.2
68
  ```
69
 
70
  Outputs with inferable metadata are allocated automatically. Explicit `outputs` entries request optional results or provide metadata that cannot be inferred from the supplied inputs and attributes.
@@ -83,7 +84,7 @@ import { getKernel } from "@huggingface/kernels";
83
 
84
  const kernel = await getKernel("webgpu-kernels/ai.onnx.Transpose", { version: 1 });
85
  // Explicit destinations request optional results or supply metadata that cannot be inferred.
86
- const { y } = await kernel({ x: { data: xData, shape: [] } }, {
87
- outputs: { y: { shape: [], dtype: "float32" } },
88
  });
89
  ```
 
40
 
41
  | Variable | Allowed dtypes |
42
  | --- | --- |
43
+ | `T` | `float32`, `float16`, `int32`, `int16`, `uint32`, `uint8`, `int8`, `bool`, `int64` |
44
 
45
  ## Implementation variants
46
 
47
  One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
48
 
49
+ - `strided_planes_vec4` — Tiles independent physical strided planes while preserving the intervening axis. Both global IO directions are contiguous inside the tile. Demoted when padding occupies more than half the tile slots.
50
  - `tiled_scalar` — Stages ragged two-dimensional transpose planes through a padded workgroup tile so reads and writes remain coalesced when vec4 alignment is unavailable.
51
  - `inner_vec4` — Vectorizes the contiguous innermost dimension for permutations that leave that axis in place. It assigns multiple vectors per invocation only when enough invocations remain to keep the dispatch populated.
52
 
 
55
  - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
56
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
57
  - [`test.json`](build/webgpu/test.json) — correctness cases
58
+ - [`bench.json`](build/webgpu/bench.json) — benchmark cases
59
  - [`datamove-flat-copy.wgsl.jinja`](build/webgpu/datamove-flat-copy.wgsl.jinja)
60
  - [`datamove-transpose-2d-tiled-scalar.wgsl.jinja`](build/webgpu/datamove-transpose-2d-tiled-scalar.wgsl.jinja)
61
  - [`datamove-transpose-2d-tiled.wgsl.jinja`](build/webgpu/datamove-transpose-2d-tiled.wgsl.jinja)
 
65
  ## Use with `@huggingface/kernels`
66
 
67
  ```sh
68
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.3
69
  ```
70
 
71
  Outputs with inferable metadata are allocated automatically. Explicit `outputs` entries request optional results or provide metadata that cannot be inferred from the supplied inputs and attributes.
 
84
 
85
  const kernel = await getKernel("webgpu-kernels/ai.onnx.Transpose", { version: 1 });
86
  // Explicit destinations request optional results or supply metadata that cannot be inferred.
87
+ const { y } = await kernel({ x: { data: xData, shape: [2, 3] } }, {
88
+ outputs: { y: { shape: [3, 2], dtype: "float32" } },
89
  });
90
  ```
build/webgpu/bench.json CHANGED
@@ -195,7 +195,7 @@
195
  "preset": "all",
196
  "provenance": {
197
  "source": "Depth Anything V2 Small neck reassemble",
198
- "notes": "Probe pair (a): the real 37x49 patch grid has 1813 rows and is not vec4-aligned. Together with pair (b), it verifies that extent parity does not determine whether the transpose is staged."
199
  },
200
  "vars": { "count": 696192, "dtype": "float32" },
201
  "attrs": { "perm": [1, 0] },
@@ -220,7 +220,7 @@
220
  "name": "transpose-f32-rank6-swin-window-partition-perm013245-pathology",
221
  "preset": "edge",
222
  "provenance": {
223
- "notes": "Rank-6 window partition (swap of the two window-grid axes with a contiguous 96-wide last axis) at a batch-8 Swin 56x56 stage. Rank 6 is outside every tiled and inner-vec4 pattern, so the generic strided kernel is the selected route; the permutation is its own inverse, so one case covers both directions."
224
  },
225
  "vars": { "count": 2408448, "dtype": "float32" },
226
  "attrs": { "perm": [0, 1, 3, 2, 4, 5] },
@@ -246,7 +246,7 @@
246
  "name": "transpose-f32-rank4-perm0321-8x64x197x64-pathology",
247
  "preset": "edge",
248
  "provenance": {
249
- "notes": "Rank-4 permutation [0,3,2,1] at a 197-token, 64-head-dim attention shape: axis 3 leaves the last position and the permutation is none of the three tiled rank-4 patterns, so the generic strided kernel is the selected route."
250
  },
251
  "vars": { "count": 6455296, "dtype": "float32" },
252
  "attrs": { "perm": [0, 3, 2, 1] },
@@ -258,13 +258,197 @@
258
  "name": "transpose-f32-rank4-perm0213-8x64x197x64-control",
259
  "preset": "edge",
260
  "provenance": {
261
- "notes": "Control for the [0,3,2,1] case: the same tensor under the attention-layout permutation [0,2,1,3] keeps the innermost axis in place and takes the inner-vec4 route."
262
  },
263
  "vars": { "count": 6455296, "dtype": "float32" },
264
  "attrs": { "perm": [0, 2, 1, 3] },
265
  "inputs": { "x": { "shape": [8, 64, 197, 64], "dtype": "float32", "dist": "normal", "seed": 157, "scale": 0.2 } },
266
  "outputs": { "y": { "shape": [8, 197, 64, 64], "dtype": "float32" } },
267
  "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  }
269
  ]
270
  }
 
195
  "preset": "all",
196
  "provenance": {
197
  "source": "Depth Anything V2 Small neck reassemble",
198
+ "notes": "A 1813x384 transpose (the 37x49 Depth Anything V2 Small patch grid) has a row count that is not a multiple of four; its 1812-row sibling in this file is the vec4-aligned control at the same column width."
199
  },
200
  "vars": { "count": 696192, "dtype": "float32" },
201
  "attrs": { "perm": [1, 0] },
 
220
  "name": "transpose-f32-rank6-swin-window-partition-perm013245-pathology",
221
  "preset": "edge",
222
  "provenance": {
223
+ "notes": "Rank-six window partition swaps a within-window axis with a window-grid axis while retaining a contiguous 96-wide final axis at a batch-eight Swin 56x56 stage. The permutation is its own inverse, so one case covers both directions."
224
  },
225
  "vars": { "count": 2408448, "dtype": "float32" },
226
  "attrs": { "perm": [0, 1, 3, 2, 4, 5] },
 
246
  "name": "transpose-f32-rank4-perm0321-8x64x197x64-pathology",
247
  "preset": "edge",
248
  "provenance": {
249
+ "notes": "Rank-four permutation [0,3,2,1] at a 197-token, 64-wide attention shape moves the final axis and checks strided output indexing."
250
  },
251
  "vars": { "count": 6455296, "dtype": "float32" },
252
  "attrs": { "perm": [0, 3, 2, 1] },
 
258
  "name": "transpose-f32-rank4-perm0213-8x64x197x64-control",
259
  "preset": "edge",
260
  "provenance": {
261
+ "notes": "Control for the [0,3,2,1] case: the same [8,64,197,64] shape under the attention-layout permutation [0,2,1,3] keeps its innermost axis."
262
  },
263
  "vars": { "count": 6455296, "dtype": "float32" },
264
  "attrs": { "perm": [0, 2, 1, 3] },
265
  "inputs": { "x": { "shape": [8, 64, 197, 64], "dtype": "float32", "dist": "normal", "seed": 157, "scale": 0.2 } },
266
  "outputs": { "y": { "shape": [8, 197, 64, 64], "dtype": "float32" } },
267
  "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
268
+ },
269
+ {
270
+ "name": "strided_large_float32_95x201x127",
271
+ "preset": "all",
272
+ "attrs": { "perm": [2, 1, 0] },
273
+ "inputs": { "x": { "shape": [95, 201, 127], "dtype": "float32", "dist": "normal", "seed": 842, "scale": 0.2 } },
274
+ "outputs": { "y": { "shape": [127, 201, 95], "dtype": "float32" } },
275
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 19400520 }] }
276
+ },
277
+ {
278
+ "name": "strided_large_float32_8x63x197x65",
279
+ "preset": "all",
280
+ "attrs": { "perm": [0, 3, 2, 1] },
281
+ "inputs": { "x": { "shape": [8, 63, 197, 65], "dtype": "float32", "dist": "normal", "seed": 842, "scale": 0.2 } },
282
+ "outputs": { "y": { "shape": [8, 65, 197, 63], "dtype": "float32" } },
283
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 51629760 }] }
284
+ },
285
+ {
286
+ "name": "strided_large_float16_95x201x127",
287
+ "preset": "all",
288
+ "attrs": { "perm": [2, 1, 0] },
289
+ "inputs": { "x": { "shape": [95, 201, 127], "dtype": "float16", "dist": "normal", "seed": 842, "scale": 0.2 } },
290
+ "outputs": { "y": { "shape": [127, 201, 95], "dtype": "float16" } },
291
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 9700260 }] }
292
+ },
293
+ {
294
+ "name": "strided_large_float16_8x63x197x65",
295
+ "preset": "all",
296
+ "attrs": { "perm": [0, 3, 2, 1] },
297
+ "inputs": { "x": { "shape": [8, 63, 197, 65], "dtype": "float16", "dist": "normal", "seed": 842, "scale": 0.2 } },
298
+ "outputs": { "y": { "shape": [8, 65, 197, 63], "dtype": "float16" } },
299
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 25814880 }] }
300
+ },
301
+ {
302
+ "name": "strided_large_float16_8x64x197x64",
303
+ "preset": "all",
304
+ "attrs": { "perm": [0, 3, 2, 1] },
305
+ "inputs": { "x": { "shape": [8, 64, 197, 64], "dtype": "float16", "dist": "normal", "seed": 842, "scale": 0.2 } },
306
+ "outputs": { "y": { "shape": [8, 64, 197, 64], "dtype": "float16" } },
307
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 25821184 }] }
308
+ },
309
+ {
310
+ "name": "strided_padding_float32_a8",
311
+ "preset": "all",
312
+ "attrs": { "perm": [0, 3, 2, 1] },
313
+ "inputs": { "x": { "shape": [16, 8, 137, 128], "dtype": "float32", "dist": "normal", "seed": 843, "scale": 0.2 } },
314
+ "outputs": { "y": { "shape": [16, 128, 137, 8], "dtype": "float32" } },
315
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 17956864 }] }
316
+ },
317
+ {
318
+ "name": "strided_padding_float32_a16",
319
+ "preset": "all",
320
+ "attrs": { "perm": [0, 3, 2, 1] },
321
+ "inputs": {
322
+ "x": { "shape": [16, 16, 137, 128], "dtype": "float32", "dist": "normal", "seed": 843, "scale": 0.2 }
323
+ },
324
+ "outputs": { "y": { "shape": [16, 128, 137, 16], "dtype": "float32" } },
325
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 35913728 }] }
326
+ },
327
+ {
328
+ "name": "strided_padding_float32_a20",
329
+ "preset": "all",
330
+ "attrs": { "perm": [0, 3, 2, 1] },
331
+ "inputs": {
332
+ "x": { "shape": [16, 20, 137, 128], "dtype": "float32", "dist": "normal", "seed": 843, "scale": 0.2 }
333
+ },
334
+ "outputs": { "y": { "shape": [16, 128, 137, 20], "dtype": "float32" } },
335
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 44892160 }] }
336
+ },
337
+ {
338
+ "name": "strided_padding_float32_a15",
339
+ "preset": "all",
340
+ "attrs": { "perm": [0, 3, 2, 1] },
341
+ "inputs": {
342
+ "x": { "shape": [16, 15, 137, 128], "dtype": "float32", "dist": "normal", "seed": 843, "scale": 0.2 }
343
+ },
344
+ "outputs": { "y": { "shape": [16, 128, 137, 15], "dtype": "float32" } },
345
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 33669120 }] }
346
+ },
347
+ {
348
+ "name": "strided_padding_float32_a17",
349
+ "preset": "all",
350
+ "attrs": { "perm": [0, 3, 2, 1] },
351
+ "inputs": {
352
+ "x": { "shape": [16, 17, 137, 128], "dtype": "float32", "dist": "normal", "seed": 843, "scale": 0.2 }
353
+ },
354
+ "outputs": { "y": { "shape": [16, 128, 137, 17], "dtype": "float32" } },
355
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 38158336 }] }
356
+ },
357
+ {
358
+ "name": "strided_padding_float16_a8",
359
+ "preset": "all",
360
+ "attrs": { "perm": [0, 3, 2, 1] },
361
+ "inputs": { "x": { "shape": [16, 8, 137, 128], "dtype": "float16", "dist": "normal", "seed": 843, "scale": 0.2 } },
362
+ "outputs": { "y": { "shape": [16, 128, 137, 8], "dtype": "float16" } },
363
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 8978432 }] }
364
+ },
365
+ {
366
+ "name": "strided_padding_float16_a15",
367
+ "preset": "all",
368
+ "attrs": { "perm": [0, 3, 2, 1] },
369
+ "inputs": {
370
+ "x": { "shape": [16, 15, 137, 128], "dtype": "float16", "dist": "normal", "seed": 843, "scale": 0.2 }
371
+ },
372
+ "outputs": { "y": { "shape": [16, 128, 137, 15], "dtype": "float16" } },
373
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 16834560 }] }
374
+ },
375
+ {
376
+ "name": "strided_padding_float16_a16",
377
+ "preset": "all",
378
+ "attrs": { "perm": [0, 3, 2, 1] },
379
+ "inputs": {
380
+ "x": { "shape": [16, 16, 137, 128], "dtype": "float16", "dist": "normal", "seed": 843, "scale": 0.2 }
381
+ },
382
+ "outputs": { "y": { "shape": [16, 128, 137, 16], "dtype": "float16" } },
383
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 17956864 }] }
384
+ },
385
+ {
386
+ "name": "strided_padding_float16_a17",
387
+ "preset": "all",
388
+ "attrs": { "perm": [0, 3, 2, 1] },
389
+ "inputs": {
390
+ "x": { "shape": [16, 17, 137, 128], "dtype": "float16", "dist": "normal", "seed": 843, "scale": 0.2 }
391
+ },
392
+ "outputs": { "y": { "shape": [16, 128, 137, 17], "dtype": "float16" } },
393
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 19079168 }] }
394
+ },
395
+ {
396
+ "name": "strided_padding_float16_a20",
397
+ "preset": "all",
398
+ "attrs": { "perm": [0, 3, 2, 1] },
399
+ "inputs": {
400
+ "x": { "shape": [16, 20, 137, 128], "dtype": "float16", "dist": "normal", "seed": 843, "scale": 0.2 }
401
+ },
402
+ "outputs": { "y": { "shape": [16, 128, 137, 20], "dtype": "float16" } },
403
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 22446080 }] }
404
+ },
405
+ {
406
+ "name": "strided_rect_float32_a47c65",
407
+ "preset": "all",
408
+ "attrs": { "perm": [0, 3, 2, 1] },
409
+ "inputs": { "x": { "shape": [8, 47, 137, 65], "dtype": "float32", "dist": "normal", "seed": 845, "scale": 0.2 } },
410
+ "outputs": { "y": { "shape": [8, 65, 137, 47], "dtype": "float32" } },
411
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 26786240 }] }
412
+ },
413
+ {
414
+ "name": "strided_rect_float32_a48c65",
415
+ "preset": "all",
416
+ "attrs": { "perm": [0, 3, 2, 1] },
417
+ "inputs": { "x": { "shape": [8, 48, 137, 65], "dtype": "float32", "dist": "normal", "seed": 845, "scale": 0.2 } },
418
+ "outputs": { "y": { "shape": [8, 65, 137, 48], "dtype": "float32" } },
419
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 27356160 }] }
420
+ },
421
+ {
422
+ "name": "strided_fulltile_float32_a33",
423
+ "preset": "all",
424
+ "attrs": { "perm": [0, 3, 2, 1] },
425
+ "inputs": { "x": { "shape": [8, 33, 137, 128], "dtype": "float32", "dist": "normal", "seed": 844, "scale": 0.2 } },
426
+ "outputs": { "y": { "shape": [8, 128, 137, 33], "dtype": "float32" } },
427
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 37036032 }] }
428
+ },
429
+ {
430
+ "name": "strided_rect_float16_a47c65",
431
+ "preset": "all",
432
+ "attrs": { "perm": [0, 3, 2, 1] },
433
+ "inputs": { "x": { "shape": [8, 47, 137, 65], "dtype": "float16", "dist": "normal", "seed": 845, "scale": 0.2 } },
434
+ "outputs": { "y": { "shape": [8, 65, 137, 47], "dtype": "float16" } },
435
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 13393120 }] }
436
+ },
437
+ {
438
+ "name": "strided_rect_float16_a48c65",
439
+ "preset": "all",
440
+ "attrs": { "perm": [0, 3, 2, 1] },
441
+ "inputs": { "x": { "shape": [8, 48, 137, 65], "dtype": "float16", "dist": "normal", "seed": 845, "scale": 0.2 } },
442
+ "outputs": { "y": { "shape": [8, 65, 137, 48], "dtype": "float16" } },
443
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 13678080 }] }
444
+ },
445
+ {
446
+ "name": "strided_fulltile_float16_a33",
447
+ "preset": "all",
448
+ "attrs": { "perm": [0, 3, 2, 1] },
449
+ "inputs": { "x": { "shape": [8, 33, 137, 128], "dtype": "float16", "dist": "normal", "seed": 844, "scale": 0.2 } },
450
+ "outputs": { "y": { "shape": [8, 128, 137, 33], "dtype": "float16" } },
451
+ "bench": { "metrics": [{ "type": "bandwidth", "value": 18518016 }] }
452
  }
453
  ]
454
  }
build/webgpu/datamove-transpose-2d-tiled-scalar.wgsl.jinja CHANGED
@@ -1,13 +1,13 @@
1
  // Tiled, optionally batched 2-D transpose over scalar elements.
2
  //
3
- // A 32x32 tile enters workgroup memory through consecutive row reads and leaves
4
  // through consecutive transposed-row writes. Guarded edges make this valid for
5
  // any extent.
6
  //
7
  // Layout contract:
8
  // - src is row-major [batch, ROWS, COLS]
9
  // - dst is row-major [batch, COLS, ROWS]
10
- // - dispatch is x = ceil(COLS / 32), y = ceil(ROWS / 32), z = batch
11
  //
12
  // The extra column changes the transposed workgroup-memory stride and reduces
13
  // bank-conflict risk on banked implementations.
@@ -16,12 +16,12 @@
16
  const ROWS: u32 = {{ rowCount }}u;
17
  const COLS: u32 = {{ cols }}u;
18
  const MAT: u32 = {{ rowCount * cols }}u;
19
- const TILE: u32 = 32u;
20
- const ROW_STEP: u32 = 8u;
21
 
22
- var<workgroup> tile: array<array<{{ scalar }}, 33>, 32>;
23
 
24
- @compute @workgroup_size(32, 8, 1)
25
  fn main(
26
  @builtin(workgroup_id) wid: vec3<u32>,
27
  @builtin(local_invocation_id) lid: vec3<u32>
@@ -30,7 +30,7 @@ fn main(
30
  let row_base = wid.y * TILE;
31
  let col_base = wid.x * TILE;
32
 
33
- // Read: lid.x runs along the source row, so a subgroup covers 32 consecutive
34
  // source elements.
35
  for (var i = 0u; i < TILE; i = i + ROW_STEP) {
36
  let r = row_base + lid.y + i;
 
1
  // Tiled, optionally batched 2-D transpose over scalar elements.
2
  //
3
+ // A TILE x TILE tile enters workgroup memory through consecutive row reads and leaves
4
  // through consecutive transposed-row writes. Guarded edges make this valid for
5
  // any extent.
6
  //
7
  // Layout contract:
8
  // - src is row-major [batch, ROWS, COLS]
9
  // - dst is row-major [batch, COLS, ROWS]
10
+ // - dispatch is x = ceil(COLS / TILE), y = ceil(ROWS / TILE), z = batch
11
  //
12
  // The extra column changes the transposed workgroup-memory stride and reduces
13
  // bank-conflict risk on banked implementations.
 
16
  const ROWS: u32 = {{ rowCount }}u;
17
  const COLS: u32 = {{ cols }}u;
18
  const MAT: u32 = {{ rowCount * cols }}u;
19
+ const TILE: u32 = {{ tileSize }}u;
20
+ const ROW_STEP: u32 = {{ tileRowStep }}u;
21
 
22
+ var<workgroup> tile: array<array<{{ scalar }}, {{ tileSize + 1 }}>, {{ tileSize }}>;
23
 
24
+ @compute @workgroup_size({{ tileSize }}, {{ tileRowStep }}, 1)
25
  fn main(
26
  @builtin(workgroup_id) wid: vec3<u32>,
27
  @builtin(local_invocation_id) lid: vec3<u32>
 
30
  let row_base = wid.y * TILE;
31
  let col_base = wid.x * TILE;
32
 
33
+ // Read: lid.x runs along the source row, so a subgroup covers consecutive
34
  // source elements.
35
  for (var i = 0u; i < TILE; i = i + ROW_STEP) {
36
  let r = row_base + lid.y + i;
build/webgpu/datamove-transpose-2d-tiled.wgsl.jinja CHANGED
@@ -1,38 +1,48 @@
1
- // Tiled 2D transpose (optionally batched) over vec4 elements.
 
 
 
 
 
2
  //
3
  // Layout contract:
4
  // - src is row-major [batch, ROWS, COLS], dst is row-major [batch, COLS, ROWS]
5
  // - ROWS % 4 == 0 and COLS % 4 == 0, so both buffers bind as vec4<T>
6
- // - dispatch: x = ceil(COLS / 32), y = ceil(ROWS / 32), z = batch
7
  //
8
- // Each 8x32 workgroup moves one 32x32 scalar tile through workgroup memory.
 
9
  // Global loads and stores each traverse consecutive vec4 addresses. The extra
10
  // workgroup-memory column changes the transposed stride and reduces
11
  // bank-conflict risk on banked implementations.
12
- {{ env.wgsl.resourceDeclarations }}
13
 
14
  const ROWS: u32 = {{ rowCount }}u;
15
  const COLS: u32 = {{ cols }}u;
16
  const ROWS_V: u32 = {{ ((rowCount / 4) | int) }}u;
17
  const COLS_V: u32 = {{ ((cols / 4) | int) }}u;
18
- const MAT_V: u32 = {{ (((rowCount * cols) / 4) | int) }}u;
19
- const TILE: u32 = 32u;
20
 
21
- var<workgroup> tile: array<array<{{ scalar }}, 33>, 32>;
22
 
23
- @compute @workgroup_size(8, 32, 1)
24
  fn main(
25
  @builtin(workgroup_id) wid: vec3<u32>,
26
  @builtin(local_invocation_id) lid: vec3<u32>
27
  ) {
28
- let batch_base = wid.z * MAT_V;
 
 
 
 
29
  let row_base = wid.y * TILE;
30
  let col_base = wid.x * TILE;
31
 
32
  let r = row_base + lid.y;
33
  let cv = (col_base / 4u) + lid.x;
34
  if (r < ROWS && cv < COLS_V) {
35
- let v = src[batch_base + r * COLS_V + cv];
36
  tile[lid.y][lid.x * 4u + 0u] = v.x;
37
  tile[lid.y][lid.x * 4u + 1u] = v.y;
38
  tile[lid.y][lid.x * 4u + 2u] = v.z;
@@ -43,7 +53,7 @@ fn main(
43
  let out_row = col_base + lid.y;
44
  let rv = (row_base / 4u) + lid.x;
45
  if (out_row < COLS && rv < ROWS_V) {
46
- dst[batch_base + out_row * ROWS_V + rv] = {{ vectorScalar }}(
47
  tile[lid.x * 4u + 0u][lid.y],
48
  tile[lid.x * 4u + 1u][lid.y],
49
  tile[lid.x * 4u + 2u][lid.y],
 
1
+ {% if transposePlaneMiddle is defined %}
2
+ // Transpose independent physical planes while retaining the intervening axis.
3
+ // Source [B, ROWS, MIDDLE, COLS] maps to [B, COLS, MIDDLE, ROWS].
4
+ // Dispatch z identifies one (batch, middle) plane; both tile IO directions
5
+ // follow contiguous addresses within that plane.
6
+ {% else %}// Tiled 2D transpose (optionally batched) over vec4 elements.
7
  //
8
  // Layout contract:
9
  // - src is row-major [batch, ROWS, COLS], dst is row-major [batch, COLS, ROWS]
10
  // - ROWS % 4 == 0 and COLS % 4 == 0, so both buffers bind as vec4<T>
11
+ // - dispatch: x = ceil(COLS / TILE), y = ceil(ROWS / TILE), z = batch
12
  //
13
+ // Each (TILE / 4) x TILE workgroup moves one TILE x TILE scalar tile through
14
+ // workgroup memory.
15
  // Global loads and stores each traverse consecutive vec4 addresses. The extra
16
  // workgroup-memory column changes the transposed stride and reduces
17
  // bank-conflict risk on banked implementations.
18
+ {% endif %}{{ env.wgsl.resourceDeclarations }}
19
 
20
  const ROWS: u32 = {{ rowCount }}u;
21
  const COLS: u32 = {{ cols }}u;
22
  const ROWS_V: u32 = {{ ((rowCount / 4) | int) }}u;
23
  const COLS_V: u32 = {{ ((cols / 4) | int) }}u;
24
+ const MAT_V: u32 = {{ (((rowCount * cols * (transposePlaneMiddle | default(1))) / 4) | int) }}u;
25
+ const TILE: u32 = {{ tileSize }}u;
26
 
27
+ var<workgroup> tile: array<array<{{ scalar }}, {{ tileSize + 1 }}>, {{ tileSize }}>;
28
 
29
+ @compute @workgroup_size({{ (tileSize / 4) | int }}, {{ tileSize }}, 1)
30
  fn main(
31
  @builtin(workgroup_id) wid: vec3<u32>,
32
  @builtin(local_invocation_id) lid: vec3<u32>
33
  ) {
34
+ {% if transposePlaneMiddle is defined %}
35
+ let source_base = (wid.z / {{ transposePlaneMiddle }}u) * MAT_V + (wid.z % {{ transposePlaneMiddle }}u) * COLS_V;
36
+ let destination_base = (wid.z / {{ transposePlaneMiddle }}u) * MAT_V + (wid.z % {{ transposePlaneMiddle }}u) * ROWS_V;
37
+ {% else %} let batch_base = wid.z * MAT_V;
38
+ {% endif %}
39
  let row_base = wid.y * TILE;
40
  let col_base = wid.x * TILE;
41
 
42
  let r = row_base + lid.y;
43
  let cv = (col_base / 4u) + lid.x;
44
  if (r < ROWS && cv < COLS_V) {
45
+ let v = src[{% if transposePlaneMiddle is defined %}source_base + r * {{ transposePlaneMiddle }}u * COLS_V{% else %}batch_base + r * COLS_V{% endif %} + cv];
46
  tile[lid.y][lid.x * 4u + 0u] = v.x;
47
  tile[lid.y][lid.x * 4u + 1u] = v.y;
48
  tile[lid.y][lid.x * 4u + 2u] = v.z;
 
53
  let out_row = col_base + lid.y;
54
  let rv = (row_base / 4u) + lid.x;
55
  if (out_row < COLS && rv < ROWS_V) {
56
+ dst[{% if transposePlaneMiddle is defined %}destination_base + out_row * {{ transposePlaneMiddle }}u * ROWS_V{% else %}batch_base + out_row * ROWS_V{% endif %} + rv] = {{ vectorScalar }}(
57
  tile[lid.x * 4u + 0u][lid.y],
58
  tile[lid.x * 4u + 1u][lid.y],
59
  tile[lid.x * 4u + 2u][lid.y],
build/webgpu/manifest.json CHANGED
@@ -5,13 +5,14 @@
5
  "inputs": { "x": { "onnx": "data", "dtype": "T" } },
6
  "outputs": { "y": { "onnx": "transposed", "dtype": "T", "rank": "ranks.x" } },
7
  "attributes": { "perm": {} },
8
- "typeConstraints": { "T": ["float32", "float16", "int32", "int16", "uint32", "uint8", "int8", "bool"] },
9
  "tunables": {
10
  "WORKGROUP_SIZE": { "default": 256 },
11
  "INNER_VEC4_ITEMS_PER_THREAD": { "default": 4 },
12
  "INNER_VEC4_MIN_THREADS": { "default": 65536 }
13
  },
14
  "derive": {
 
15
  "dtypeSupported": "f16Ok(dtypes.T)",
16
  "rank2IO": "ranks.x == 2 and ranks.y == 2",
17
  "rank3IO": "ranks.x == 3 and ranks.y == 3",
@@ -31,15 +32,15 @@
31
  "innerVec4Rank4Ok": "explicitRank4 and attrs.perm[3] == 3 and dim(shapes.x, 3) % 4 == 0 and numel(shapes.y) > 0 and dtypeSupported",
32
  "innerVec4Ok": "innerVec4Rank3Ok or innerVec4Rank4Ok",
33
  "innerVec4Perm": "attrs.perm",
34
- "tiled2dShapeOk": "(defaultRank2 or (explicitRank2 and attrs.perm[0] == 1 and attrs.perm[1] == 0)) and numel(shapes.x) > 0 and ceil(dim(shapes.x, 0) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
35
  "tiled2dOk": "tiled2dShapeOk and dim(shapes.x, 0) % 4 == 0 and dim(shapes.x, 1) % 4 == 0",
36
- "tiled3dShapeOk": "explicitRank3 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 2) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
37
  "tiled3dOk": "tiled3dShapeOk and dim(shapes.x, 1) % 4 == 0 and dim(shapes.x, 2) % 4 == 0",
38
- "tiled4dShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 3 and attrs.perm[3] == 2 and numel(shapes.x) > 0 and dim(shapes.x, 0) * dim(shapes.x, 1) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 2) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 3) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
39
  "tiled4dOk": "tiled4dShapeOk and dim(shapes.x, 2) % 4 == 0 and dim(shapes.x, 3) % 4 == 0",
40
- "tiled5dNcdhwToNdhwcShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 4 and attrs.perm[4] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4)) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
41
  "tiled5dNcdhwToNdhwcOk": "tiled5dNcdhwToNdhwcShapeOk and dim(shapes.x, 1) % 4 == 0 and (dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4)) % 4 == 0",
42
- "tiled5dNdhwcToNcdhwShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 4 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and attrs.perm[4] == 3 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 4) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
43
  "tiled5dNdhwcToNcdhwOk": "tiled5dNdhwcToNcdhwShapeOk and dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) % 4 == 0 and dim(shapes.x, 4) % 4 == 0",
44
  "memcpyPerm3": "explicitRank3 and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2])",
45
  "memcpyPerm4": "explicitRank4 and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[0] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[1] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[2]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[2] < attrs.perm[3])",
@@ -47,64 +48,99 @@
47
  "memcpyPermOk": "memcpyPerm3 or memcpyPerm4 or memcpyPerm5",
48
  "identityPermutation": "genericTransposeOk and sameShape(genericPerm, reversedAxes(ranks.x) | reverse)",
49
  "identityCopyOk": "numel(shapes.y) > 0 and numel(shapes.y) % 4 == 0 and (identityPermutation or memcpyPermOk) and dtypeSupported",
50
- "tiled4dNchwToNhwcShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 2) * dim(shapes.x, 3)) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
51
  "tiled4dNchwToNhwcOk": "tiled4dNchwToNhwcShapeOk and dim(shapes.x, 1) % 4 == 0 and (dim(shapes.x, 2) * dim(shapes.x, 3)) % 4 == 0",
52
- "tiled4dNhwcToNchwShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 3 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 1) * dim(shapes.x, 2)) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 3) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
53
  "tiled4dNhwcToNchwOk": "tiled4dNhwcToNchwShapeOk and (dim(shapes.x, 1) * dim(shapes.x, 2)) % 4 == 0 and dim(shapes.x, 3) % 4 == 0",
54
  "tiledTransposeOk": "tiled2dOk or tiled3dOk or tiled4dOk or tiled5dNcdhwToNdhwcOk or tiled5dNdhwcToNcdhwOk or tiled4dNchwToNhwcOk or tiled4dNhwcToNchwOk",
55
  "tiledScalarTransposeOk": "tiled2dShapeOk or tiled3dShapeOk or tiled4dShapeOk or tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk",
56
  "tiledRows": "dim(shapes.x, 0) if tiled2dShapeOk else (dim(shapes.x, 1) if tiled3dShapeOk else (dim(shapes.x, 2) if tiled4dShapeOk else (dim(shapes.x, 1) if tiled5dNcdhwToNdhwcShapeOk else (dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) if tiled5dNdhwcToNcdhwShapeOk else (dim(shapes.x, 1) if tiled4dNchwToNhwcShapeOk else (dim(shapes.x, 1) * dim(shapes.x, 2) if tiled4dNhwcToNchwShapeOk else 1))))))",
57
  "tiledCols": "dim(shapes.x, 1) if tiled2dShapeOk else (dim(shapes.x, 2) if tiled3dShapeOk else (dim(shapes.x, 3) if tiled4dShapeOk else (dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4) if tiled5dNcdhwToNdhwcShapeOk else (dim(shapes.x, 4) if tiled5dNdhwcToNcdhwShapeOk else (dim(shapes.x, 2) * dim(shapes.x, 3) if tiled4dNchwToNhwcShapeOk else (dim(shapes.x, 3) if tiled4dNhwcToNchwShapeOk else 1))))))",
58
- "tiledBatch": "1 if tiled2dShapeOk else (dim(shapes.x, 0) if tiled3dShapeOk else (dim(shapes.x, 0) * dim(shapes.x, 1) if tiled4dShapeOk else (dim(shapes.x, 0) if tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk else 1)))"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  },
60
  "bindings": {
61
- "src": { "arg": "x", "buffer": "read-only-storage", "elementType": "$vectorScalar" },
62
- "dst": { "arg": "y", "buffer": "storage", "elementType": "$vectorScalar" },
63
- "x": { "buffer": "read-only-storage", "elementType": "$scalar" },
64
- "y": { "buffer": "storage", "elementType": "$scalar" }
65
  },
66
  "variants": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  {
68
  "id": "tiled_vec4",
69
  "priority": 22,
70
- "when": ["tiledTransposeOk"],
71
- "derive": { "scalar": "dtypes.T", "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
72
  "passes": [
73
  {
74
  "id": "main",
75
- "name": "Transpose.tiled",
76
  "shader": "datamove-transpose-2d-tiled.wgsl.jinja",
77
  "derive": { "rowCount": "tiledRows", "cols": "tiledCols" },
78
  "bindings": ["src", "dst"],
79
- "dispatch": { "x": "ceil(tiledCols / 32)", "y": "ceil(tiledRows / 32)", "z": "tiledBatch" }
80
  }
81
  ]
82
  },
83
  {
84
  "id": "tiled_scalar",
85
  "priority": 19,
86
- "when": ["tiledScalarTransposeOk", "dtypeSupported"],
87
- "derive": { "scalar": "dtypes.T" },
88
  "passes": [
89
  {
90
  "id": "main",
91
- "name": "Transpose.tiledScalar",
92
  "shader": "datamove-transpose-2d-tiled-scalar.wgsl.jinja",
93
- "derive": { "rowCount": "tiledRows", "cols": "tiledCols" },
94
  "bindings": ["x", "y"],
95
- "dispatch": { "x": "ceil(tiledCols / 32)", "y": "ceil(tiledRows / 32)", "z": "tiledBatch" }
96
  }
97
  ]
98
  },
99
  {
100
  "id": "identity_vec4",
101
  "priority": 21,
102
- "when": ["identityCopyOk"],
103
- "derive": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
104
  "passes": [
105
  {
106
  "id": "main",
107
- "name": "Transpose.identityVec4",
108
  "shader": "datamove-flat-copy.wgsl.jinja",
109
  "derive": { "count": "numel(shapes.y) / 4" },
110
  "bindings": ["src", "dst"],
@@ -119,20 +155,13 @@
119
  {
120
  "id": "inner_vec4",
121
  "priority": 20,
122
- "when": ["innerVec4Ok"],
123
- "derive": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
124
  "passes": [
125
  {
126
  "id": "main",
127
- "name": "Transpose.innerVec4",
128
  "shader": "datamove-transpose-vec4.wgsl.jinja",
129
- "derive": {
130
- "inputShape": "shapes.x",
131
- "outputShape": "shapes.y",
132
- "rank": "ranks.x",
133
- "perm": "innerVec4Perm",
134
- "count": "numel(shapes.y) / 4"
135
- },
136
  "bindings": ["src", "dst"],
137
  "dispatch": {
138
  "x": "min(ceilDiv((max(min(numel(shapes.y) / 4, tunables.INNER_VEC4_MIN_THREADS), ceilDiv(numel(shapes.y) / 4, tunables.INNER_VEC4_ITEMS_PER_THREAD))), (tunables.WORKGROUP_SIZE)), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
@@ -145,19 +174,12 @@
145
  {
146
  "id": "generic",
147
  "when": ["genericTransposeOk", "dtypeSupported"],
148
- "derive": { "scalar": "dtypes.T" },
149
  "passes": [
150
  {
151
  "id": "main",
152
  "name": "Transpose",
153
  "shader": "transpose.wgsl.jinja",
154
- "derive": {
155
- "inputShape": "shapes.x",
156
- "outputShape": "shapes.y",
157
- "rank": "ranks.x",
158
- "perm": "genericPerm",
159
- "itemsPerInvocation": 4
160
- },
161
  "bindings": [
162
  "x",
163
  "y",
 
5
  "inputs": { "x": { "onnx": "data", "dtype": "T" } },
6
  "outputs": { "y": { "onnx": "transposed", "dtype": "T", "rank": "ranks.x" } },
7
  "attributes": { "perm": {} },
8
+ "typeConstraints": { "T": ["float32", "float16", "int32", "int16", "uint32", "uint8", "int8", "bool", "int64"] },
9
  "tunables": {
10
  "WORKGROUP_SIZE": { "default": 256 },
11
  "INNER_VEC4_ITEMS_PER_THREAD": { "default": 4 },
12
  "INNER_VEC4_MIN_THREADS": { "default": 65536 }
13
  },
14
  "derive": {
15
+ "tileSize": 32,
16
  "dtypeSupported": "f16Ok(dtypes.T)",
17
  "rank2IO": "ranks.x == 2 and ranks.y == 2",
18
  "rank3IO": "ranks.x == 3 and ranks.y == 3",
 
32
  "innerVec4Rank4Ok": "explicitRank4 and attrs.perm[3] == 3 and dim(shapes.x, 3) % 4 == 0 and numel(shapes.y) > 0 and dtypeSupported",
33
  "innerVec4Ok": "innerVec4Rank3Ok or innerVec4Rank4Ok",
34
  "innerVec4Perm": "attrs.perm",
35
+ "tiled2dShapeOk": "(defaultRank2 or (explicitRank2 and attrs.perm[0] == 1 and attrs.perm[1] == 0)) and numel(shapes.x) > 0 and ceil(dim(shapes.x, 0) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
36
  "tiled2dOk": "tiled2dShapeOk and dim(shapes.x, 0) % 4 == 0 and dim(shapes.x, 1) % 4 == 0",
37
+ "tiled3dShapeOk": "explicitRank3 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 2) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
38
  "tiled3dOk": "tiled3dShapeOk and dim(shapes.x, 1) % 4 == 0 and dim(shapes.x, 2) % 4 == 0",
39
+ "tiled4dShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 3 and attrs.perm[3] == 2 and numel(shapes.x) > 0 and dim(shapes.x, 0) * dim(shapes.x, 1) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 2) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 3) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
40
  "tiled4dOk": "tiled4dShapeOk and dim(shapes.x, 2) % 4 == 0 and dim(shapes.x, 3) % 4 == 0",
41
+ "tiled5dNcdhwToNdhwcShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 4 and attrs.perm[4] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4)) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
42
  "tiled5dNcdhwToNdhwcOk": "tiled5dNcdhwToNdhwcShapeOk and dim(shapes.x, 1) % 4 == 0 and (dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4)) % 4 == 0",
43
+ "tiled5dNdhwcToNcdhwShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 4 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and attrs.perm[4] == 3 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 4) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
44
  "tiled5dNdhwcToNcdhwOk": "tiled5dNdhwcToNcdhwShapeOk and dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) % 4 == 0 and dim(shapes.x, 4) % 4 == 0",
45
  "memcpyPerm3": "explicitRank3 and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2])",
46
  "memcpyPerm4": "explicitRank4 and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[0] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[1] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[2]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[2] < attrs.perm[3])",
 
48
  "memcpyPermOk": "memcpyPerm3 or memcpyPerm4 or memcpyPerm5",
49
  "identityPermutation": "genericTransposeOk and sameShape(genericPerm, reversedAxes(ranks.x) | reverse)",
50
  "identityCopyOk": "numel(shapes.y) > 0 and numel(shapes.y) % 4 == 0 and (identityPermutation or memcpyPermOk) and dtypeSupported",
51
+ "tiled4dNchwToNhwcShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 2) * dim(shapes.x, 3)) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
52
  "tiled4dNchwToNhwcOk": "tiled4dNchwToNhwcShapeOk and dim(shapes.x, 1) % 4 == 0 and (dim(shapes.x, 2) * dim(shapes.x, 3)) % 4 == 0",
53
+ "tiled4dNhwcToNchwShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 3 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 1) * dim(shapes.x, 2)) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 3) / tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
54
  "tiled4dNhwcToNchwOk": "tiled4dNhwcToNchwShapeOk and (dim(shapes.x, 1) * dim(shapes.x, 2)) % 4 == 0 and dim(shapes.x, 3) % 4 == 0",
55
  "tiledTransposeOk": "tiled2dOk or tiled3dOk or tiled4dOk or tiled5dNcdhwToNdhwcOk or tiled5dNdhwcToNcdhwOk or tiled4dNchwToNhwcOk or tiled4dNhwcToNchwOk",
56
  "tiledScalarTransposeOk": "tiled2dShapeOk or tiled3dShapeOk or tiled4dShapeOk or tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk",
57
  "tiledRows": "dim(shapes.x, 0) if tiled2dShapeOk else (dim(shapes.x, 1) if tiled3dShapeOk else (dim(shapes.x, 2) if tiled4dShapeOk else (dim(shapes.x, 1) if tiled5dNcdhwToNdhwcShapeOk else (dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) if tiled5dNdhwcToNcdhwShapeOk else (dim(shapes.x, 1) if tiled4dNchwToNhwcShapeOk else (dim(shapes.x, 1) * dim(shapes.x, 2) if tiled4dNhwcToNchwShapeOk else 1))))))",
58
  "tiledCols": "dim(shapes.x, 1) if tiled2dShapeOk else (dim(shapes.x, 2) if tiled3dShapeOk else (dim(shapes.x, 3) if tiled4dShapeOk else (dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4) if tiled5dNcdhwToNdhwcShapeOk else (dim(shapes.x, 4) if tiled5dNdhwcToNcdhwShapeOk else (dim(shapes.x, 2) * dim(shapes.x, 3) if tiled4dNchwToNhwcShapeOk else (dim(shapes.x, 3) if tiled4dNhwcToNchwShapeOk else 1))))))",
59
+ "tiledBatch": "1 if tiled2dShapeOk else (dim(shapes.x, 0) if tiled3dShapeOk else (dim(shapes.x, 0) * dim(shapes.x, 1) if tiled4dShapeOk else (dim(shapes.x, 0) if tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk else 1)))",
60
+ "stridedPlaneRank3": "rank3IO and genericTransposeOk and genericPerm[0] == 2 and genericPerm[1] == 1 and genericPerm[2] == 0",
61
+ "stridedPlaneRank4": "rank4IO and genericTransposeOk and genericPerm[0] == 0 and genericPerm[1] == 3 and genericPerm[2] == 2 and genericPerm[3] == 1",
62
+ "stridedPlaneRows": "dim(shapes.x, 0) if stridedPlaneRank3 else dim(shapes.x, 1) if stridedPlaneRank4 else 1",
63
+ "stridedPlaneCols": "dim(shapes.x, 2) if stridedPlaneRank3 else dim(shapes.x, 3) if stridedPlaneRank4 else 1",
64
+ "stridedPlaneMiddle": "dim(shapes.x, 1) if stridedPlaneRank3 else dim(shapes.x, 2) if stridedPlaneRank4 else 1",
65
+ "stridedPlaneBatch": "1 if stridedPlaneRank3 else dim(shapes.x, 0) if stridedPlaneRank4 else 1",
66
+ "stridedPlaneGroups": "stridedPlaneBatch * stridedPlaneMiddle",
67
+ "stridedPlaneOk": "(stridedPlaneRank3 or stridedPlaneRank4) and numel(shapes.x) > 0 and dtypeSupported and ceilDiv(stridedPlaneRows, tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceilDiv(stridedPlaneCols, tileSize) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and stridedPlaneGroups <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and device.limits.maxComputeInvocationsPerWorkgroup >= tileSize * tileSize / 4 and device.limits.maxComputeWorkgroupStorageSize >= tileSize * (tileSize + 1) * (2 if dtypes.T == \"f16\" else 4)",
68
+ "stridedPlaneSparseTile": "ceilDiv(stridedPlaneRows, tileSize) * ceilDiv(stridedPlaneCols, tileSize) * tileSize * tileSize > 2 * stridedPlaneRows * stridedPlaneCols",
69
+ "scalar": "dtypes.T",
70
+ "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
71
+ "inputShape": "shapes.x",
72
+ "outputShape": "shapes.y",
73
+ "rank": "ranks.x"
74
  },
75
  "bindings": {
76
+ "src": { "arg": "x", "elementType": "$vectorScalar" },
77
+ "dst": { "arg": "y", "elementType": "$vectorScalar" },
78
+ "x": { "elementType": "$scalar" },
79
+ "y": { "elementType": "$scalar" }
80
  },
81
  "variants": [
82
+ {
83
+ "id": "strided_planes_vec4",
84
+ "priority": 22,
85
+ "when": ["dtypes.T != \"vec2<u32>\"", "stridedPlaneOk", "stridedPlaneRows % 4 == 0 and stridedPlaneCols % 4 == 0", "device.limits.maxComputeWorkgroupSizeX >= tileSize / 4", "device.limits.maxComputeWorkgroupSizeY >= tileSize"],
86
+ "passes": [
87
+ {
88
+ "id": "main",
89
+ "name": "Transpose.Tiled",
90
+ "shader": "datamove-transpose-2d-tiled.wgsl.jinja",
91
+ "derive": {
92
+ "rowCount": "stridedPlaneRows",
93
+ "cols": "stridedPlaneCols",
94
+ "transposePlaneMiddle": "stridedPlaneMiddle"
95
+ },
96
+ "bindings": ["src", "dst"],
97
+ "dispatch": {
98
+ "x": "ceilDiv(stridedPlaneCols, tileSize)",
99
+ "y": "ceilDiv(stridedPlaneRows, tileSize)",
100
+ "z": "stridedPlaneGroups"
101
+ }
102
+ }
103
+ ],
104
+ "demoteWhen": ["stridedPlaneSparseTile"]
105
+ },
106
  {
107
  "id": "tiled_vec4",
108
  "priority": 22,
109
+ "when": ["dtypes.T != \"vec2<u32>\"", "tiledTransposeOk"],
 
110
  "passes": [
111
  {
112
  "id": "main",
113
+ "name": "Transpose.Tiled",
114
  "shader": "datamove-transpose-2d-tiled.wgsl.jinja",
115
  "derive": { "rowCount": "tiledRows", "cols": "tiledCols" },
116
  "bindings": ["src", "dst"],
117
+ "dispatch": { "x": "ceil(tiledCols / tileSize)", "y": "ceil(tiledRows / tileSize)", "z": "tiledBatch" }
118
  }
119
  ]
120
  },
121
  {
122
  "id": "tiled_scalar",
123
  "priority": 19,
124
+ "when": ["dtypes.T != \"vec2<u32>\"", "tiledScalarTransposeOk", "dtypeSupported"],
 
125
  "passes": [
126
  {
127
  "id": "main",
128
+ "name": "Transpose.TiledScalar",
129
  "shader": "datamove-transpose-2d-tiled-scalar.wgsl.jinja",
130
+ "derive": { "rowCount": "tiledRows", "cols": "tiledCols", "tileRowStep": 8 },
131
  "bindings": ["x", "y"],
132
+ "dispatch": { "x": "ceil(tiledCols / tileSize)", "y": "ceil(tiledRows / tileSize)", "z": "tiledBatch" }
133
  }
134
  ]
135
  },
136
  {
137
  "id": "identity_vec4",
138
  "priority": 21,
139
+ "when": ["dtypes.T != \"vec2<u32>\"", "identityCopyOk"],
 
140
  "passes": [
141
  {
142
  "id": "main",
143
+ "name": "Transpose.IdentityVec4",
144
  "shader": "datamove-flat-copy.wgsl.jinja",
145
  "derive": { "count": "numel(shapes.y) / 4" },
146
  "bindings": ["src", "dst"],
 
155
  {
156
  "id": "inner_vec4",
157
  "priority": 20,
158
+ "when": ["dtypes.T != \"vec2<u32>\"", "innerVec4Ok"],
 
159
  "passes": [
160
  {
161
  "id": "main",
162
+ "name": "Transpose.InnerVec4",
163
  "shader": "datamove-transpose-vec4.wgsl.jinja",
164
+ "derive": { "perm": "innerVec4Perm", "count": "numel(shapes.y) / 4" },
 
 
 
 
 
 
165
  "bindings": ["src", "dst"],
166
  "dispatch": {
167
  "x": "min(ceilDiv((max(min(numel(shapes.y) / 4, tunables.INNER_VEC4_MIN_THREADS), ceilDiv(numel(shapes.y) / 4, tunables.INNER_VEC4_ITEMS_PER_THREAD))), (tunables.WORKGROUP_SIZE)), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
 
174
  {
175
  "id": "generic",
176
  "when": ["genericTransposeOk", "dtypeSupported"],
 
177
  "passes": [
178
  {
179
  "id": "main",
180
  "name": "Transpose",
181
  "shader": "transpose.wgsl.jinja",
182
+ "derive": { "perm": "genericPerm", "itemsPerInvocation": 4 },
 
 
 
 
 
 
183
  "bindings": [
184
  "x",
185
  "y",
build/webgpu/metadata.json CHANGED
@@ -1,26 +1,27 @@
1
  {
2
  "name": "ai.onnx.Transpose",
3
- "id": "_ai_onnx_transpose_webgpu_2a9c5f1",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "ClUFa7Y4uL69eUy46pTK4YciV/+jJxlDxSJFK+/JU6A=",
11
  "datamove-flat-copy.wgsl.jinja": "g9d62mer2bmHfbScqX5CIT5Zq/Pdinac0W2dH0CD+5s=",
12
- "datamove-transpose-2d-tiled-scalar.wgsl.jinja": "ycF9afS0CqHrLIfFi6hAqp+7GIcnHHAyN74hSF+/FrY=",
13
- "datamove-transpose-2d-tiled.wgsl.jinja": "GNVRxhagDFVQ0DGjsUGD8BVzktDHsELwe0qw4xRHD60=",
14
  "datamove-transpose-vec4.wgsl.jinja": "NARpwfOEcl8GRu17lnq62KXK/mgh61Jj3YHfi8Zofik=",
15
- "manifest.json": "F4FH/KuY12tkUnIKlE3wBmydDgzgzAbfPva12DYYGBY=",
16
- "test.json": "rQerBft2NMuE0I+S7GErvy8rPRoUQxOkI3NvNH5dcKc=",
17
- "transpose.wgsl.jinja": "a9TDelnkVrpe1XBA8rTAurSGrTmIQ6BO16ZHRJWiQJw="
18
  }
19
  },
20
- "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
21
  "webgpu": {
22
- "manifestSpec": "2.0",
23
  "variants": {
 
24
  "tiled_vec4": ["datamove-transpose-2d-tiled.wgsl.jinja"],
25
  "tiled_scalar": ["datamove-transpose-2d-tiled-scalar.wgsl.jinja"],
26
  "identity_vec4": ["datamove-flat-copy.wgsl.jinja"],
 
1
  {
2
  "name": "ai.onnx.Transpose",
3
+ "id": "_ai_onnx_transpose_webgpu_4ac33c9",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "Z+G8ub4uWe/9LpOzIYhV6tWU29vMRvN3Ajhc3H+po0Q=",
11
  "datamove-flat-copy.wgsl.jinja": "g9d62mer2bmHfbScqX5CIT5Zq/Pdinac0W2dH0CD+5s=",
12
+ "datamove-transpose-2d-tiled-scalar.wgsl.jinja": "JhTsa8OYjMAUAKUDAkFxnxTQyr/f8FpW783nwX6PWZk=",
13
+ "datamove-transpose-2d-tiled.wgsl.jinja": "T7WQJsuQ85hTO/F+QQ8GOXsvBP80iJmD0s5cRgPHG4U=",
14
  "datamove-transpose-vec4.wgsl.jinja": "NARpwfOEcl8GRu17lnq62KXK/mgh61Jj3YHfi8Zofik=",
15
+ "manifest.json": "alujpyF6YtHpSBUjwlWNjNi2z8k0Et7TUtb+yKu+Wm8=",
16
+ "test.json": "llE4r29U/p+9XIkG4zoy/1KeOXaQ2dk32PpU8GNcrS4=",
17
+ "transpose.wgsl.jinja": "l11cGbVOuaD1SwWqAhmKpkVs/rWCOm/YO6zFao2YZ4E="
18
  }
19
  },
20
+ "provenance": { "kernel": { "sha": "6fdf6301e2bbcc2f03bf1eaf493b7ad55ef33afc", "dirty": false } },
21
  "webgpu": {
22
+ "manifestSpec": "2.1",
23
  "variants": {
24
+ "strided_planes_vec4": ["datamove-transpose-2d-tiled.wgsl.jinja"],
25
  "tiled_vec4": ["datamove-transpose-2d-tiled.wgsl.jinja"],
26
  "tiled_scalar": ["datamove-transpose-2d-tiled-scalar.wgsl.jinja"],
27
  "identity_vec4": ["datamove-flat-copy.wgsl.jinja"],
build/webgpu/test.json CHANGED
@@ -2,7 +2,12 @@
2
  "fixtureArrays": {
3
  "onnx_backend_transpose_input_x": [0.54881352186203, 0.7151893377304077, 0.6027633547782898, 0.5448831915855408, 0.42365479469299316, 0.6458941102027893, 0.4375872015953064, 0.891772985458374, 0.9636627435684204, 0.3834415078163147, 0.7917250394821167, 0.5288949012756348, 0.5680445432662964, 0.9255966544151306, 0.07103605568408966, 0.08712930232286453, 0.020218396559357643, 0.832619845867157, 0.7781567573547363, 0.8700121641159058, 0.978618323802948, 0.7991585731506348, 0.4614793658256531, 0.7805292010307312],
4
  "ort_transpose021_f32_input_x": [1, 2, 3, 4, 5, 6, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 1.3, 2.3, 3.3, 4.3, 5.3, 6.3],
5
- "ort_nhwc_to_nchw_uint32_input_x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
 
 
 
 
 
6
  },
7
  "cases": [
8
  {
@@ -906,7 +911,7 @@
906
  {
907
  "name": "tiled_2d_vec4_naninf_signedzero_payload_preserved",
908
  "provenance": {
909
- "notes": "Aligned [8,8] perm[1,0] f32 selects tiled_vec4 (both dims %4==0, %32 partial). Verifies NaN / +-Inf / signed-zero survive the vec4->scalar->workgroup->vec4 staging bit-exact. Expected values are the [8,8] input transposed (column-major read)."
910
  },
911
  "attrs": { "perm": [1, 0] },
912
  "inputs": {
@@ -935,7 +940,7 @@
935
  {
936
  "name": "scalar_rank4_default_reverse_naninf_preserved",
937
  "provenance": {
938
- "notes": "No perm => default full reversal [3,2,1,0] on the generic scalar path. Values placed so index [i,j,k,l] holds 8*i+4*j+2*k+l; output[l,k,j,i] = input[i,j,k,l]. NaN/Inf/-0 payloads at select positions must survive the scalar input_offset gather."
939
  },
940
  "inputs": {
941
  "x": {
@@ -963,7 +968,7 @@
963
  {
964
  "name": "rank5_ncdhw_to_ndhwc_tiled_vec4",
965
  "provenance": {
966
- "notes": "Compact route lock for the flattened batched-2D NCDHW-to-NDHWC tile. C=4 and D*H*W=32 exercise one complete spatial tile boundary while preserving exact copy semantics."
967
  },
968
  "attrs": { "perm": [0, 2, 3, 4, 1] },
969
  "inputs": {
@@ -978,7 +983,7 @@
978
  {
979
  "name": "rank5_ndhwc_to_ncdhw_tiled_candidate",
980
  "provenance": {
981
- "notes": "Inverse rank-5 layout lock: NDHWC [N,D,H,W,C] becomes NCDHW via perm [0,4,1,2,3]. C=4 and D*H*W=32 match one flattened 2D transpose boundary with bit-exact copy semantics."
982
  },
983
  "attrs": { "perm": [0, 4, 1, 2, 3] },
984
  "inputs": {
@@ -1123,6 +1128,453 @@
1123
  }
1124
  },
1125
  "outputs": { "y": { "dtype": "float32", "shape": [4, 2, 2, 4, 2, 2, 3, 2], "tolerance": 0 } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1126
  }
1127
  ]
1128
  }
 
2
  "fixtureArrays": {
3
  "onnx_backend_transpose_input_x": [0.54881352186203, 0.7151893377304077, 0.6027633547782898, 0.5448831915855408, 0.42365479469299316, 0.6458941102027893, 0.4375872015953064, 0.891772985458374, 0.9636627435684204, 0.3834415078163147, 0.7917250394821167, 0.5288949012756348, 0.5680445432662964, 0.9255966544151306, 0.07103605568408966, 0.08712930232286453, 0.020218396559357643, 0.832619845867157, 0.7781567573547363, 0.8700121641159058, 0.978618323802948, 0.7991585731506348, 0.4614793658256531, 0.7805292010307312],
4
  "ort_transpose021_f32_input_x": [1, 2, 3, 4, 5, 6, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 1.3, 2.3, 3.3, 4.3, 5.3, 6.3],
5
+ "ort_nhwc_to_nchw_uint32_input_x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
6
+ "strided_f32": [0, 1e-40, -1e-40, 0.125, -3.5, 100000000000000000000, -100000000000000000000, 17.25, -31.5, 0.5, 7.75, -0.75, 2],
7
+ "strided_f16": [0, 5.960464477539063e-8, -5.960464477539063e-8, 0.125, -3.5, 65504, -65504, 17.25, -31.5, 0.5, 7.75, -0.75, 2],
8
+ "strided_signed": [-63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 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],
9
+ "strided_unsigned": [0, 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],
10
+ "strided_bool": [0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0]
11
  },
12
  "cases": [
13
  {
 
911
  {
912
  "name": "tiled_2d_vec4_naninf_signedzero_payload_preserved",
913
  "provenance": {
914
+ "notes": "An aligned [8,8] transpose with perm=[1,0] must carry NaN, both infinities, and zeros exactly to their transposed positions."
915
  },
916
  "attrs": { "perm": [1, 0] },
917
  "inputs": {
 
940
  {
941
  "name": "scalar_rank4_default_reverse_naninf_preserved",
942
  "provenance": {
943
+ "notes": "With perm omitted, the default full reversal [3,2,1,0] maps output[l,k,j,i] to input[i,j,k,l]. NaN and both infinities must land exactly at their reversed positions."
944
  },
945
  "inputs": {
946
  "x": {
 
968
  {
969
  "name": "rank5_ncdhw_to_ndhwc_tiled_vec4",
970
  "provenance": {
971
+ "notes": "NCDHW-to-NDHWC transpose with four channels and 32 spatial positions checks exact batched copy semantics."
972
  },
973
  "attrs": { "perm": [0, 2, 3, 4, 1] },
974
  "inputs": {
 
983
  {
984
  "name": "rank5_ndhwc_to_ncdhw_tiled_candidate",
985
  "provenance": {
986
+ "notes": "A rank-5 NDHWC-to-NCDHW transpose (perm [0,4,1,2,3]) moves the size-4 channel axis to position 1, with the D, H, and W dimensions (2x4x4=32 elements) kept contiguous; distinct linspace values at every position make a wrong index mapping observable."
987
  },
988
  "attrs": { "perm": [0, 4, 1, 2, 3] },
989
  "inputs": {
 
1128
  }
1129
  },
1130
  "outputs": { "y": { "dtype": "float32", "shape": [4, 2, 2, 4, 2, 2, 3, 2], "tolerance": 0 } }
1131
+ },
1132
+ {
1133
+ "name": "strided_planes_ragged_batched_float32",
1134
+ "inputs": {
1135
+ "x": {
1136
+ "shape": [2, 47, 3, 63],
1137
+ "dtype": "float32",
1138
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1139
+ }
1140
+ },
1141
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1142
+ "attrs": { "perm": [0, 3, 2, 1] }
1143
+ },
1144
+ {
1145
+ "name": "strided_planes_ragged_batched_float16",
1146
+ "inputs": {
1147
+ "x": {
1148
+ "shape": [2, 47, 3, 63],
1149
+ "dtype": "float16",
1150
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1151
+ }
1152
+ },
1153
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1154
+ "attrs": { "perm": [0, 3, 2, 1] }
1155
+ },
1156
+ {
1157
+ "name": "strided_planes_ragged_batched_int32",
1158
+ "inputs": {
1159
+ "x": {
1160
+ "shape": [2, 47, 3, 63],
1161
+ "dtype": "int32",
1162
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_signed" } }
1163
+ }
1164
+ },
1165
+ "outputs": { "y": { "dtype": "int32", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1166
+ "attrs": { "perm": [0, 3, 2, 1] }
1167
+ },
1168
+ {
1169
+ "name": "strided_planes_ragged_batched_int16",
1170
+ "inputs": {
1171
+ "x": {
1172
+ "shape": [2, 47, 3, 63],
1173
+ "dtype": "int16",
1174
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_signed" } }
1175
+ }
1176
+ },
1177
+ "outputs": { "y": { "dtype": "int16", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1178
+ "attrs": { "perm": [0, 3, 2, 1] }
1179
+ },
1180
+ {
1181
+ "name": "strided_planes_ragged_batched_uint32",
1182
+ "inputs": {
1183
+ "x": {
1184
+ "shape": [2, 47, 3, 63],
1185
+ "dtype": "uint32",
1186
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_unsigned" } }
1187
+ }
1188
+ },
1189
+ "outputs": { "y": { "dtype": "uint32", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1190
+ "attrs": { "perm": [0, 3, 2, 1] }
1191
+ },
1192
+ {
1193
+ "name": "strided_planes_ragged_batched_uint8",
1194
+ "inputs": {
1195
+ "x": {
1196
+ "shape": [2, 47, 3, 63],
1197
+ "dtype": "uint8",
1198
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_unsigned" } }
1199
+ }
1200
+ },
1201
+ "outputs": { "y": { "dtype": "uint8", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1202
+ "attrs": { "perm": [0, 3, 2, 1] }
1203
+ },
1204
+ {
1205
+ "name": "strided_planes_ragged_batched_int8",
1206
+ "inputs": {
1207
+ "x": {
1208
+ "shape": [2, 47, 3, 63],
1209
+ "dtype": "int8",
1210
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_signed" } }
1211
+ }
1212
+ },
1213
+ "outputs": { "y": { "dtype": "int8", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1214
+ "attrs": { "perm": [0, 3, 2, 1] }
1215
+ },
1216
+ {
1217
+ "name": "strided_planes_ragged_batched_bool",
1218
+ "inputs": {
1219
+ "x": {
1220
+ "shape": [2, 47, 3, 63],
1221
+ "dtype": "bool",
1222
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_bool" } }
1223
+ }
1224
+ },
1225
+ "outputs": { "y": { "dtype": "bool", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1226
+ "attrs": { "perm": [0, 3, 2, 1] }
1227
+ },
1228
+ {
1229
+ "name": "strided_planes_rank3_default_aligned_float32",
1230
+ "inputs": {
1231
+ "x": {
1232
+ "shape": [16, 3, 32],
1233
+ "dtype": "float32",
1234
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1235
+ }
1236
+ },
1237
+ "outputs": { "y": { "dtype": "float32", "shape": [32, 3, 16], "tolerance": 0, "relTolerance": 0 } }
1238
+ },
1239
+ {
1240
+ "name": "strided_planes_half_full_tile_float32",
1241
+ "inputs": {
1242
+ "x": {
1243
+ "shape": [2, 16, 3, 32],
1244
+ "dtype": "float32",
1245
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1246
+ }
1247
+ },
1248
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 3, 16], "tolerance": 0, "relTolerance": 0 } },
1249
+ "attrs": { "perm": [0, 3, 2, 1] }
1250
+ },
1251
+ {
1252
+ "name": "strided_planes_padding_demoted_boundary_float32",
1253
+ "inputs": {
1254
+ "x": {
1255
+ "shape": [2, 15, 3, 32],
1256
+ "dtype": "float32",
1257
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1258
+ }
1259
+ },
1260
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 3, 15], "tolerance": 0, "relTolerance": 0 } },
1261
+ "attrs": { "perm": [0, 3, 2, 1] }
1262
+ },
1263
+ {
1264
+ "name": "strided_planes_skinny_demoted_float32",
1265
+ "inputs": {
1266
+ "x": {
1267
+ "shape": [2, 8, 5, 64],
1268
+ "dtype": "float32",
1269
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1270
+ }
1271
+ },
1272
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 5, 8], "tolerance": 0, "relTolerance": 0 } },
1273
+ "attrs": { "perm": [0, 3, 2, 1] }
1274
+ },
1275
+ {
1276
+ "name": "strided_planes_partial_scalar_demoted_float32",
1277
+ "inputs": {
1278
+ "x": {
1279
+ "shape": [2, 17, 3, 32],
1280
+ "dtype": "float32",
1281
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1282
+ }
1283
+ },
1284
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 3, 17], "tolerance": 0, "relTolerance": 0 } },
1285
+ "attrs": { "perm": [0, 3, 2, 1] }
1286
+ },
1287
+ {
1288
+ "name": "strided_planes_rank3_explicit_ragged_float32",
1289
+ "inputs": {
1290
+ "x": {
1291
+ "shape": [47, 3, 63],
1292
+ "dtype": "float32",
1293
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1294
+ }
1295
+ },
1296
+ "outputs": { "y": { "dtype": "float32", "shape": [63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1297
+ "attrs": { "perm": [2, 1, 0] }
1298
+ },
1299
+ {
1300
+ "name": "strided_planes_aligned_nan_float32",
1301
+ "inputs": {
1302
+ "x": {
1303
+ "shape": [2, 16, 3, 32],
1304
+ "dtype": "float32",
1305
+ "data": {
1306
+ "kind": "cycle",
1307
+ "values": { "$ref": "#/fixtureArrays/strided_f32" },
1308
+ "nanStart": 3.0,
1309
+ "nanCount": 2.0
1310
+ }
1311
+ }
1312
+ },
1313
+ "outputs": {
1314
+ "y": { "dtype": "float32", "shape": [2, 32, 3, 16], "tolerance": 0, "relTolerance": 0, "allowNaN": true }
1315
+ },
1316
+ "attrs": { "perm": [0, 3, 2, 1] }
1317
+ },
1318
+ {
1319
+ "name": "strided_planes_ragged_nan_float32",
1320
+ "inputs": {
1321
+ "x": {
1322
+ "shape": [2, 47, 3, 63],
1323
+ "dtype": "float32",
1324
+ "data": {
1325
+ "kind": "cycle",
1326
+ "values": { "$ref": "#/fixtureArrays/strided_f32" },
1327
+ "nanStart": 3.0,
1328
+ "nanCount": 2.0
1329
+ }
1330
+ }
1331
+ },
1332
+ "outputs": {
1333
+ "y": { "dtype": "float32", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0, "allowNaN": true }
1334
+ },
1335
+ "attrs": { "perm": [0, 3, 2, 1] }
1336
+ },
1337
+ {
1338
+ "name": "strided_planes_rank3_default_aligned_float16",
1339
+ "inputs": {
1340
+ "x": {
1341
+ "shape": [16, 3, 32],
1342
+ "dtype": "float16",
1343
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1344
+ }
1345
+ },
1346
+ "outputs": { "y": { "dtype": "float16", "shape": [32, 3, 16], "tolerance": 0, "relTolerance": 0 } }
1347
+ },
1348
+ {
1349
+ "name": "strided_planes_half_full_tile_float16",
1350
+ "inputs": {
1351
+ "x": {
1352
+ "shape": [2, 16, 3, 32],
1353
+ "dtype": "float16",
1354
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1355
+ }
1356
+ },
1357
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 3, 16], "tolerance": 0, "relTolerance": 0 } },
1358
+ "attrs": { "perm": [0, 3, 2, 1] }
1359
+ },
1360
+ {
1361
+ "name": "strided_planes_padding_demoted_boundary_float16",
1362
+ "inputs": {
1363
+ "x": {
1364
+ "shape": [2, 15, 3, 32],
1365
+ "dtype": "float16",
1366
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1367
+ }
1368
+ },
1369
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 3, 15], "tolerance": 0, "relTolerance": 0 } },
1370
+ "attrs": { "perm": [0, 3, 2, 1] }
1371
+ },
1372
+ {
1373
+ "name": "strided_planes_skinny_demoted_float16",
1374
+ "inputs": {
1375
+ "x": {
1376
+ "shape": [2, 8, 5, 64],
1377
+ "dtype": "float16",
1378
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1379
+ }
1380
+ },
1381
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 5, 8], "tolerance": 0, "relTolerance": 0 } },
1382
+ "attrs": { "perm": [0, 3, 2, 1] }
1383
+ },
1384
+ {
1385
+ "name": "strided_planes_partial_scalar_demoted_float16",
1386
+ "inputs": {
1387
+ "x": {
1388
+ "shape": [2, 17, 3, 32],
1389
+ "dtype": "float16",
1390
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1391
+ }
1392
+ },
1393
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 3, 17], "tolerance": 0, "relTolerance": 0 } },
1394
+ "attrs": { "perm": [0, 3, 2, 1] }
1395
+ },
1396
+ {
1397
+ "name": "strided_planes_rank3_explicit_ragged_float16",
1398
+ "inputs": {
1399
+ "x": {
1400
+ "shape": [47, 3, 63],
1401
+ "dtype": "float16",
1402
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1403
+ }
1404
+ },
1405
+ "outputs": { "y": { "dtype": "float16", "shape": [63, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1406
+ "attrs": { "perm": [2, 1, 0] }
1407
+ },
1408
+ {
1409
+ "name": "strided_planes_aligned_nan_float16",
1410
+ "inputs": {
1411
+ "x": {
1412
+ "shape": [2, 16, 3, 32],
1413
+ "dtype": "float16",
1414
+ "data": {
1415
+ "kind": "cycle",
1416
+ "values": { "$ref": "#/fixtureArrays/strided_f16" },
1417
+ "nanStart": 3.0,
1418
+ "nanCount": 2.0
1419
+ }
1420
+ }
1421
+ },
1422
+ "outputs": {
1423
+ "y": { "dtype": "float16", "shape": [2, 32, 3, 16], "tolerance": 0, "relTolerance": 0, "allowNaN": true }
1424
+ },
1425
+ "attrs": { "perm": [0, 3, 2, 1] }
1426
+ },
1427
+ {
1428
+ "name": "strided_planes_ragged_nan_float16",
1429
+ "inputs": {
1430
+ "x": {
1431
+ "shape": [2, 47, 3, 63],
1432
+ "dtype": "float16",
1433
+ "data": {
1434
+ "kind": "cycle",
1435
+ "values": { "$ref": "#/fixtureArrays/strided_f16" },
1436
+ "nanStart": 3.0,
1437
+ "nanCount": 2.0
1438
+ }
1439
+ }
1440
+ },
1441
+ "outputs": {
1442
+ "y": { "dtype": "float16", "shape": [2, 63, 3, 47], "tolerance": 0, "relTolerance": 0, "allowNaN": true }
1443
+ },
1444
+ "attrs": { "perm": [0, 3, 2, 1] }
1445
+ },
1446
+ {
1447
+ "name": "strided_planes_empty_outer_float32",
1448
+ "inputs": {
1449
+ "x": {
1450
+ "shape": [0, 3, 4],
1451
+ "dtype": "float32",
1452
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1453
+ }
1454
+ },
1455
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 3, 0], "tolerance": 0, "relTolerance": 0 } }
1456
+ },
1457
+ {
1458
+ "name": "strided_planes_empty_middle_float32",
1459
+ "inputs": {
1460
+ "x": {
1461
+ "shape": [2, 16, 0, 32],
1462
+ "dtype": "float32",
1463
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1464
+ }
1465
+ },
1466
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 0, 16], "tolerance": 0, "relTolerance": 0 } },
1467
+ "attrs": { "perm": [0, 3, 2, 1] }
1468
+ },
1469
+ {
1470
+ "name": "strided_planes_full_scalar_tile_boundary_float32",
1471
+ "inputs": {
1472
+ "x": {
1473
+ "shape": [2, 33, 3, 64],
1474
+ "dtype": "float32",
1475
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1476
+ }
1477
+ },
1478
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 3, 33], "tolerance": 0, "relTolerance": 0 } },
1479
+ "attrs": { "perm": [0, 3, 2, 1] }
1480
+ },
1481
+ {
1482
+ "name": "strided_planes_full_scalar_tile_boundary_float16",
1483
+ "inputs": {
1484
+ "x": {
1485
+ "shape": [2, 33, 3, 64],
1486
+ "dtype": "float16",
1487
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1488
+ }
1489
+ },
1490
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 3, 33], "tolerance": 0, "relTolerance": 0 } },
1491
+ "attrs": { "perm": [0, 3, 2, 1] }
1492
+ },
1493
+ {
1494
+ "name": "strided_planes_two_axis_padding_47_float32",
1495
+ "inputs": {
1496
+ "x": {
1497
+ "shape": [2, 47, 3, 65],
1498
+ "dtype": "float32",
1499
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1500
+ }
1501
+ },
1502
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 65, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1503
+ "attrs": { "perm": [0, 3, 2, 1] }
1504
+ },
1505
+ {
1506
+ "name": "strided_planes_two_axis_padding_48_float32",
1507
+ "inputs": {
1508
+ "x": {
1509
+ "shape": [2, 48, 3, 65],
1510
+ "dtype": "float32",
1511
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f32" } }
1512
+ }
1513
+ },
1514
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 65, 3, 48], "tolerance": 0, "relTolerance": 0 } },
1515
+ "attrs": { "perm": [0, 3, 2, 1] }
1516
+ },
1517
+ {
1518
+ "name": "strided_planes_two_axis_padding_47_float16",
1519
+ "inputs": {
1520
+ "x": {
1521
+ "shape": [2, 47, 3, 65],
1522
+ "dtype": "float16",
1523
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1524
+ }
1525
+ },
1526
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 65, 3, 47], "tolerance": 0, "relTolerance": 0 } },
1527
+ "attrs": { "perm": [0, 3, 2, 1] }
1528
+ },
1529
+ {
1530
+ "name": "strided_planes_two_axis_padding_48_float16",
1531
+ "inputs": {
1532
+ "x": {
1533
+ "shape": [2, 48, 3, 65],
1534
+ "dtype": "float16",
1535
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/strided_f16" } }
1536
+ }
1537
+ },
1538
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 65, 3, 48], "tolerance": 0, "relTolerance": 0 } },
1539
+ "attrs": { "perm": [0, 3, 2, 1] }
1540
+ },
1541
+ {
1542
+ "name": "int64_full_range_f32_subnormal_rank2_preserve_data",
1543
+ "provenance": {
1544
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
1545
+ "test": "TransposeOpTest.TwoDim",
1546
+ "notes": "Transpose is data movement with reindexing; selected finite subnormal values must survive the permutation."
1547
+ },
1548
+ "attrs": { "perm": [1, 0] },
1549
+ "inputs": {
1550
+ "x": {
1551
+ "dtype": "int64",
1552
+ "shape": [2, 3],
1553
+ "data": {
1554
+ "kind": "cycle",
1555
+ "values": ["0", "4294967296", "8589934593", "-4294967297", "9223372036854775807", "-9223372036854775808", "9223372036854775806", "-1"]
1556
+ }
1557
+ }
1558
+ },
1559
+ "outputs": { "y": { "dtype": "int64", "shape": [3, 2], "tolerance": 0, "relTolerance": 0 } },
1560
+ "tolerance": 0,
1561
+ "relTolerance": 0
1562
+ },
1563
+ {
1564
+ "name": "int64_full_range_rank1_default_perm",
1565
+ "inputs": {
1566
+ "x": {
1567
+ "dtype": "int64",
1568
+ "shape": [7],
1569
+ "data": {
1570
+ "kind": "cycle",
1571
+ "values": ["4294967296", "8589934593", "-4294967297", "9223372036854775807", "-9223372036854775808", "9223372036854775806", "-1", "0"]
1572
+ }
1573
+ }
1574
+ },
1575
+ "outputs": { "y": { "dtype": "int64", "shape": [7], "tolerance": 0, "relTolerance": 0 } },
1576
+ "tolerance": 0,
1577
+ "relTolerance": 0
1578
  }
1579
  ]
1580
  }
build/webgpu/transpose.wgsl.jinja CHANGED
@@ -12,9 +12,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
12
  for (var i = begin; i < end; i = i + 1u) {
13
  {%- endmacro %}
14
  {% macro flat_tail_close() %}
15
- }
16
- {% endmacro %}
17
-
18
  {{ env.wgsl.resourceDeclarations }}
19
 
20
  fn input_offset({% if rank > 0 %}out_index: u32{% endif %}) -> u32 {
@@ -42,5 +40,5 @@ fn input_offset({% if rank > 0 %}out_index: u32{% endif %}) -> u32 {
42
 
43
  {{ flat_tail_open() }}
44
  y[i] = x[input_offset({% if rank > 0 %}i{% endif %})];
45
- {{ flat_tail_close() -}}
46
  }
 
12
  for (var i = begin; i < end; i = i + 1u) {
13
  {%- endmacro %}
14
  {% macro flat_tail_close() %}
15
+ }{% endmacro %}
 
 
16
  {{ env.wgsl.resourceDeclarations }}
17
 
18
  fn input_offset({% if rank > 0 %}out_index: u32{% endif %}) -> u32 {
 
40
 
41
  {{ flat_tail_open() }}
42
  y[i] = x[input_offset({% if rank > 0 %}i{% endif %})];
43
+ {{ flat_tail_close() }}
44
  }