Xenova HF Staff commited on
Commit
b35cc85
·
verified ·
1 Parent(s): d77c310

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,16 +18,16 @@ See the [ONNX `Pad` spec](https://onnx.ai/onnx/operators/onnx__Pad.html) for the
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
- | --- | --- | --- | --- | --- | --- | --- |
23
- | `data` | `data` | `T` | — | — | Input tensor to be padded. | required |
24
- | `constant_value` | `constant_value` | `T` | `0` | — | Optional scalar value used as the fill constant when `mode` is `constant`; defaults to 0. | optional |
25
 
26
  ## Outputs
27
 
28
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
29
- | --- | --- | --- | --- | --- | --- | --- |
30
- | `output` | `output` | `T` | same as `data` | — | Tensor after padding, with each axis enlarged by the corresponding begin and end pad counts. | required |
31
 
32
  ## Attributes
33
 
@@ -46,7 +46,7 @@ Attributes and default values (overridable per request):
46
 
47
  ## Files
48
 
49
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
50
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
51
  - [`test.json`](build/webgpu/test.json) — correctness cases
52
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -55,15 +55,18 @@ Attributes and default values (overridable per request):
55
 
56
  ## Use with `@huggingface/kernels`
57
 
58
- The loader automatically allocates outputs whose metadata it can derive from the manifest contract and this call.
 
 
59
 
60
- The explicit `outputs` entries provide shape and logical dtype metadata for the results listed below:
61
 
62
- - `output`
63
 
64
- Each entry either requests an optional result or supplies metadata that cannot be inferred from the inputs.
65
 
66
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
67
 
68
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
69
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- |
23
+ | `data` | `T` | — | — | Input tensor to be padded. | required |
24
+ | `constant_value` | `T` | `0` | — | Optional scalar value used as the fill constant when `mode` is `constant`; defaults to 0. | optional |
25
 
26
  ## Outputs
27
 
28
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
29
+ | --- | --- | --- | --- | --- | --- |
30
+ | `output` | `T` | same as `data` | — | Tensor after padding, with each axis enlarged by the corresponding begin and end pad counts. | required |
31
 
32
  ## Attributes
33
 
 
46
 
47
  ## Files
48
 
49
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
50
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
51
  - [`test.json`](build/webgpu/test.json) — correctness cases
52
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
55
 
56
  ## Use with `@huggingface/kernels`
57
 
58
+ ```sh
59
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
60
+ ```
61
 
62
+ 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.
63
 
64
+ This example supplies explicit metadata for:
65
 
66
+ - `output`
67
 
68
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
69
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
70
 
71
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
72
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Pad",
3
  "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
4
  "cases": [
5
  {
 
1
  {
 
2
  "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
3
  "cases": [
4
  {
build/webgpu/manifest.json CHANGED
@@ -2,139 +2,54 @@
2
  "domain": "ai.onnx",
3
  "name": "Pad",
4
  "sinceVersion": 19,
5
- "description": "Pads a tensor along each axis using one of four modes: `constant` (fill with a scalar value), `reflect` (mirror edge values), `edge` (replicate boundary values), or `wrap` (torus-like wrap-around). Supply the required ONNX `pads` tensor, and optional `axes` tensor when present, through the `pads` request attribute as a full-rank list. Negative counts crop the corresponding edge.",
6
- "inputs": [
7
- { "role": "data", "dtype": "T", "description": "Input tensor to be padded." },
8
- {
9
- "role": "constant_value",
10
- "dtype": "T",
11
- "rank": 0,
12
- "optional": true,
13
- "description": "Optional scalar value used as the fill constant when `mode` is `constant`; defaults to 0."
14
- }
15
- ],
16
- "outputs": [
17
- {
18
- "role": "output",
19
- "dtype": "T",
20
- "description": "Tensor after padding, with each axis enlarged by the corresponding begin and end pad counts.",
21
- "rank": "ranks.data"
22
- }
23
- ],
24
- "attributes": { "mode": "constant" },
25
- "attributeDescriptions": {
26
- "pads": "Values of the required `pads` tensor, with an optional `axes` tensor expanded to full rank, supplied as `[x1_begin, ..., xN_begin, x1_end, ..., xN_end]`.",
27
- "mode": "Padding mode: `constant` (default), `reflect`, `edge`, or `wrap`."
28
- },
29
  "attributeConstraints": {
30
  "pads": { "required": true },
31
  "mode": { "values": ["constant", "reflect", "edge", "wrap"] }
32
  },
33
  "typeConstraints": { "T": ["float32", "float16", "uint32", "int32", "uint8", "int8", "bool"] },
34
- "args": {
35
- "data": { "kind": "tensor", "semantic": "data", "role": "input" },
36
- "constant_value": { "kind": "tensor", "semantic": "constant_value", "role": "constant_value", "required": false },
37
- "output": { "kind": "tensor", "semantic": "output", "role": "output" }
38
- },
39
- "tunables": { "WORKGROUP_SIZE": 256 },
40
  "derive": {
41
  "shapeContract": "ranks.data == ranks.output and ranks.output >= 1",
42
  "defaultValueContract": "not present.constant_value and shapeContract and f16Ok(dtypes.T)",
43
- "vec4Contract": "defaultValueContract and numel(shapes.output) > 0 and dim(shapes.output, ranks.output - 1) % 4 == 0"
 
 
44
  },
45
- "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"", "mode": "attrs.mode" },
46
- "bindingSets": {
47
- "valueInput": [
48
- {
49
- "name": "data",
50
- "arg": "data",
51
- "semantic": "data",
52
- "buffer": { "type": "read-only-storage" },
53
- "elementType": "$scalar"
54
- },
55
- {
56
- "name": "padValue",
57
- "arg": "constant_value",
58
- "semantic": "constant_value",
59
- "buffer": { "type": "read-only-storage" },
60
- "elementType": "$scalar",
61
- "length": 1
62
- },
63
- {
64
- "name": "output",
65
- "arg": "output",
66
- "semantic": "output",
67
- "buffer": { "type": "storage" },
68
- "elementType": "$scalar"
69
- },
70
- {
71
- "name": "params",
72
- "semantic": "kernel.params",
73
- "buffer": { "type": "uniform" },
74
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output)" }] }
75
- }
76
- ],
77
- "copy": [
78
- {
79
- "name": "data",
80
- "arg": "data",
81
- "semantic": "data",
82
- "buffer": { "type": "read-only-storage" },
83
- "elementType": "$scalar"
84
- },
85
- {
86
- "name": "output",
87
- "arg": "output",
88
- "semantic": "output",
89
- "buffer": { "type": "storage" },
90
- "elementType": "$outputElement"
91
- }
92
- ],
93
- "scalarPad": [
94
- {
95
- "name": "data",
96
- "arg": "data",
97
- "semantic": "data",
98
- "buffer": { "type": "read-only-storage" },
99
- "elementType": "$scalar"
100
- },
101
- {
102
- "name": "output",
103
- "arg": "output",
104
- "semantic": "output",
105
- "buffer": { "type": "storage" },
106
- "elementType": "$scalar"
107
- },
108
- {
109
- "name": "params",
110
- "semantic": "kernel.params",
111
- "buffer": { "type": "uniform" },
112
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output)" }] }
113
- }
114
- ]
115
  },
116
  "variants": [
117
  {
118
  "id": "constant_value_input",
119
  "priority": 30,
120
  "when": ["present.constant_value", "numel(shapes.constant_value) == 1", "shapeContract", "f16Ok(dtypes.T)"],
121
- "constants": { "valueFromInput": true },
122
  "passes": [
123
  {
124
  "id": "main",
125
  "name": "Pad",
126
- "source": {
127
- "shader": "pad.wgsl.jinja",
128
- "inputs": {
129
- "dataShape": "shapes.data",
130
- "outputShape": "shapes.output",
131
- "rank": "ranks.output",
132
- "pads": "attrs.pads",
133
- "itemsPerInvocation": 4
134
- }
135
  },
136
- "bindings": "valueInput",
137
- "dispatch": { "threads": "ceilDiv(numel(shapes.output), 4)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
 
 
 
 
138
  }
139
  ]
140
  },
@@ -142,47 +57,51 @@
142
  "id": "quad_vec4",
143
  "priority": 11,
144
  "when": ["vec4Contract"],
145
- "constants": { "outputElement": "\"vec4<\" ~ dtypes.T ~ \">\"", "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
146
  "passes": [
147
  {
148
  "id": "main",
149
  "name": "Pad.quadVec4",
150
- "source": {
151
- "shader": "pad-quad-vec4.wgsl.jinja",
152
- "inputs": {
153
- "dataShape": "shapes.data",
154
- "outputShape": "shapes.output",
155
- "rank": "ranks.output",
156
- "pads": "attrs.pads",
157
- "value": 0
158
- }
159
  },
160
- "bindings": "copy",
161
- "dispatch": { "gridStride": "numel(shapes.output) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
162
  }
163
  ]
164
  },
165
  {
166
  "id": "constant_rank1_to_4",
167
  "when": ["defaultValueContract"],
168
- "constants": { "valueFromInput": false },
169
  "passes": [
170
  {
171
  "id": "main",
172
  "name": "Pad",
173
- "source": {
174
- "shader": "pad.wgsl.jinja",
175
- "inputs": {
176
- "dataShape": "shapes.data",
177
- "outputShape": "shapes.output",
178
- "rank": "ranks.output",
179
- "pads": "attrs.pads",
180
- "value": 0,
181
- "itemsPerInvocation": 4
182
- }
183
  },
184
- "bindings": "scalarPad",
185
- "dispatch": { "threads": "ceilDiv(numel(shapes.output), 4)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
186
  }
187
  ]
188
  }
 
2
  "domain": "ai.onnx",
3
  "name": "Pad",
4
  "sinceVersion": 19,
5
+ "inputs": { "data": { "dtype": "T" }, "constant_value": { "dtype": "T", "rank": 0, "optional": true } },
6
+ "outputs": { "output": { "dtype": "T", "rank": "ranks.data" } },
7
+ "attributes": { "mode": { "default": "constant" }, "pads": {} },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  "attributeConstraints": {
9
  "pads": { "required": true },
10
  "mode": { "values": ["constant", "reflect", "edge", "wrap"] }
11
  },
12
  "typeConstraints": { "T": ["float32", "float16", "uint32", "int32", "uint8", "int8", "bool"] },
13
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 } },
 
 
 
 
 
14
  "derive": {
15
  "shapeContract": "ranks.data == ranks.output and ranks.output >= 1",
16
  "defaultValueContract": "not present.constant_value and shapeContract and f16Ok(dtypes.T)",
17
+ "vec4Contract": "defaultValueContract and numel(shapes.output) > 0 and dim(shapes.output, ranks.output - 1) % 4 == 0",
18
+ "scalar": "dtypes.T",
19
+ "mode": "attrs.mode"
20
  },
21
+ "bindings": {
22
+ "params": { "buffer": "uniform", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.output)" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  },
24
  "variants": [
25
  {
26
  "id": "constant_value_input",
27
  "priority": 30,
28
  "when": ["present.constant_value", "numel(shapes.constant_value) == 1", "shapeContract", "f16Ok(dtypes.T)"],
29
+ "derive": { "valueFromInput": true },
30
  "passes": [
31
  {
32
  "id": "main",
33
  "name": "Pad",
34
+ "shader": "pad.wgsl.jinja",
35
+ "derive": {
36
+ "dataShape": "shapes.data",
37
+ "outputShape": "shapes.output",
38
+ "rank": "ranks.output",
39
+ "pads": "attrs.pads",
40
+ "itemsPerInvocation": 4
 
 
41
  },
42
+ "bindings": [
43
+ "data",
44
+ { "arg": "constant_value", "name": "padValue", "elementType": "$scalar", "length": 1 },
45
+ "output",
46
+ "params"
47
+ ],
48
+ "dispatch": {
49
+ "x": "min(ceilDiv((ceilDiv(numel(shapes.output), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
50
+ "y": "ceilDiv(ceilDiv((ceilDiv(numel(shapes.output), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
51
+ "z": 1
52
+ }
53
  }
54
  ]
55
  },
 
57
  "id": "quad_vec4",
58
  "priority": 11,
59
  "when": ["vec4Contract"],
60
+ "derive": { "outputElement": "\"vec4<\" ~ dtypes.T ~ \">\"", "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
61
  "passes": [
62
  {
63
  "id": "main",
64
  "name": "Pad.quadVec4",
65
+ "shader": "pad-quad-vec4.wgsl.jinja",
66
+ "derive": {
67
+ "dataShape": "shapes.data",
68
+ "outputShape": "shapes.output",
69
+ "rank": "ranks.output",
70
+ "pads": "attrs.pads",
71
+ "value": 0
 
 
72
  },
73
+ "bindings": ["data", { "arg": "output", "elementType": "$outputElement" }],
74
+ "dispatch": {
75
+ "x": "min(ceilDiv((numel(shapes.output) / 4), (tunables.WORKGROUP_SIZE)), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
76
+ "y": 1,
77
+ "z": 1
78
+ }
79
  }
80
  ]
81
  },
82
  {
83
  "id": "constant_rank1_to_4",
84
  "when": ["defaultValueContract"],
85
+ "derive": { "valueFromInput": false },
86
  "passes": [
87
  {
88
  "id": "main",
89
  "name": "Pad",
90
+ "shader": "pad.wgsl.jinja",
91
+ "derive": {
92
+ "dataShape": "shapes.data",
93
+ "outputShape": "shapes.output",
94
+ "rank": "ranks.output",
95
+ "pads": "attrs.pads",
96
+ "value": 0,
97
+ "itemsPerInvocation": 4
 
 
98
  },
99
+ "bindings": ["data", "output", "params"],
100
+ "dispatch": {
101
+ "x": "min(ceilDiv((ceilDiv(numel(shapes.output), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
102
+ "y": "ceilDiv(ceilDiv((ceilDiv(numel(shapes.output), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
103
+ "z": 1
104
+ }
105
  }
106
  ]
107
  }
build/webgpu/metadata.json CHANGED
@@ -1,19 +1,26 @@
1
  {
2
  "name": "ai.onnx.Pad",
3
- "id": "_ai_onnx_pad_webgpu_df32065",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "c5OFqXWDpHEMBxvQhbwziuREKzcvtHq7AD3mAWc/O/w=",
11
- "manifest.json": "uxY+ml18prHKbui31BK4eUR7F4cesNrM9HuEeRpdSKc=",
12
- "pad-quad-vec4.wgsl.jinja": "1CGMCSmy+Suuii9slZko0MXqHiIf0+iAb8UjG/mKLkU=",
13
- "pad.wgsl.jinja": "J1ZYf3L07tN15khGSOR1teZ15QsxAFv3MjiBrAu4ykk=",
14
- "test.json": "9NikPawZjt0OZY+069sKOCAJ/K5Xc7F6usOSJ6TrvpE="
15
  }
16
  },
17
- "provenance": { "kernel": { "sha": "c928d21e6cc1310861cba3bafb75f5f679ecf5f3", "dirty": false } },
18
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Pad" }
 
 
 
 
 
 
 
19
  }
 
1
  {
2
  "name": "ai.onnx.Pad",
3
+ "id": "_ai_onnx_pad_webgpu_30935c6",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "8tPycaU74JVcyHJOuvQfZrz8BEwiW5yt868REE1nvEI=",
11
+ "manifest.json": "izmrfOCxDl4LceDqRx5gtmM5Q1dfUM4oJcnITWPO8mI=",
12
+ "pad-quad-vec4.wgsl.jinja": "nKXmrJKUYEPgQR1ugQHSwvGjzW1gaerShR3b8fHLnoA=",
13
+ "pad.wgsl.jinja": "uAqBSKweEQls2Ml+WxW1nkrsPhVMvLOYPNTtGcTwSgM=",
14
+ "test.json": "pw+IPrkqhtk9kFg2+5puugkg5AE10N7CwLH0HWkNtks="
15
  }
16
  },
17
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
18
+ "webgpu": {
19
+ "manifestSpec": "2.0",
20
+ "variants": {
21
+ "constant_value_input": ["pad.wgsl.jinja"],
22
+ "quad_vec4": ["pad-quad-vec4.wgsl.jinja"],
23
+ "constant_rank1_to_4": ["pad.wgsl.jinja"]
24
+ }
25
+ }
26
  }
build/webgpu/pad-quad-vec4.wgsl.jinja CHANGED
@@ -6,12 +6,9 @@
6
  // map_coord returns -1 outside bounds in constant mode and for empty extents.
7
  // Its reflect period and crop_start/sliced_dim arithmetic define the exact
8
  // coordinate mapping.
9
- {% if usesF16 %}
10
- enable f16;
11
- {% endif %}
12
  {{ env.wgsl.resourceDeclarations }}
13
 
14
- {% set rank = source.rank %}
15
  fn map_coord(coord: i32, dim: i32) -> i32 {
16
  if (coord >= 0i && coord < dim) {
17
  return coord;
@@ -43,19 +40,19 @@ fn map_coord(coord: i32, dim: i32) -> i32 {
43
 
44
  {% set countVecs = namespace(value=1) %}
45
  {% for j in range(rank) %}
46
- {% set countVecs.value = countVecs.value * source.outputShape[j] %}
47
  {% endfor %}
48
  {% set countVecs.value = ((countVecs.value / 4) | int) %}
49
- {% set lastPre = source.pads[rank - 1] %}
50
- {% set lastPost = source.pads[2 * rank - 1] %}
51
  {% set lastCrop = (0 - lastPre) if lastPre < 0 else 0 %}
52
  {% set lastTrim = (0 - lastPost) if lastPost < 0 else 0 %}
53
- {% set lastSlicedRaw = source.dataShape[rank - 1] - lastCrop - lastTrim %}
54
  {% set lastSliced = lastSlicedRaw if lastSlicedRaw > 0 else 0 %}
55
  {% set lastPad = lastPre if lastPre > 0 else 0 %}
56
  const COUNT: u32 = {{ countVecs.value }}u;
57
  const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
58
- const OUT_W_VECS: u32 = {{ ((source.outputShape[rank - 1] / 4) | int) }}u;
59
  const PAD_W: i32 = {{ lastPad }}i;
60
  const CROP_W: i32 = {{ lastCrop }}i;
61
  const SLICED_W: i32 = {{ lastSliced }}i;
@@ -65,7 +62,7 @@ fn main(
65
  @builtin(global_invocation_id) gid: vec3<u32>,
66
  @builtin(num_workgroups) nwg: vec3<u32>
67
  ) {
68
- let fill = {{ scalar }}({{ source.value }});
69
  let stride = nwg.x * WG;
70
  for (var v = gid.x; v < COUNT; v += stride) {
71
  let ow0 = i32((v % OUT_W_VECS) * 4u);
@@ -77,18 +74,18 @@ fn main(
77
  {% for axis in range(rank - 1) %}
78
  {% set ostride = namespace(value=1) %}
79
  {% for j in range(axis + 1, rank - 1) %}
80
- {% set ostride.value = ostride.value * source.outputShape[j] %}
81
  {% endfor %}
82
  {% set safeOStride = ostride.value %}
83
  {% set dstride = namespace(value=1) %}
84
  {% for j in range(axis + 1, rank) %}
85
- {% set dstride.value = dstride.value * source.dataShape[j] %}
86
  {% endfor %}
87
- {% set pre = source.pads[axis] %}
88
- {% set post = source.pads[axis + rank] %}
89
  {% set crop = (0 - pre) if pre < 0 else 0 %}
90
  {% set trim = (0 - post) if post < 0 else 0 %}
91
- {% set slicedRaw = source.dataShape[axis] - crop - trim %}
92
  {% set sliced = slicedRaw if slicedRaw > 0 else 0 %}
93
  {% set padBefore = pre if pre > 0 else 0 %}
94
  let out_coord{{ axis }} = i32(rem / {{ safeOStride }}u);
 
6
  // map_coord returns -1 outside bounds in constant mode and for empty extents.
7
  // Its reflect period and crop_start/sliced_dim arithmetic define the exact
8
  // coordinate mapping.
 
 
 
9
  {{ env.wgsl.resourceDeclarations }}
10
 
11
+ {% set rank = rank %}
12
  fn map_coord(coord: i32, dim: i32) -> i32 {
13
  if (coord >= 0i && coord < dim) {
14
  return coord;
 
40
 
41
  {% set countVecs = namespace(value=1) %}
42
  {% for j in range(rank) %}
43
+ {% set countVecs.value = countVecs.value * outputShape[j] %}
44
  {% endfor %}
45
  {% set countVecs.value = ((countVecs.value / 4) | int) %}
46
+ {% set lastPre = pads[rank - 1] %}
47
+ {% set lastPost = pads[2 * rank - 1] %}
48
  {% set lastCrop = (0 - lastPre) if lastPre < 0 else 0 %}
49
  {% set lastTrim = (0 - lastPost) if lastPost < 0 else 0 %}
50
+ {% set lastSlicedRaw = dataShape[rank - 1] - lastCrop - lastTrim %}
51
  {% set lastSliced = lastSlicedRaw if lastSlicedRaw > 0 else 0 %}
52
  {% set lastPad = lastPre if lastPre > 0 else 0 %}
53
  const COUNT: u32 = {{ countVecs.value }}u;
54
  const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
55
+ const OUT_W_VECS: u32 = {{ ((outputShape[rank - 1] / 4) | int) }}u;
56
  const PAD_W: i32 = {{ lastPad }}i;
57
  const CROP_W: i32 = {{ lastCrop }}i;
58
  const SLICED_W: i32 = {{ lastSliced }}i;
 
62
  @builtin(global_invocation_id) gid: vec3<u32>,
63
  @builtin(num_workgroups) nwg: vec3<u32>
64
  ) {
65
+ let fill = {{ scalar }}({{ value }});
66
  let stride = nwg.x * WG;
67
  for (var v = gid.x; v < COUNT; v += stride) {
68
  let ow0 = i32((v % OUT_W_VECS) * 4u);
 
74
  {% for axis in range(rank - 1) %}
75
  {% set ostride = namespace(value=1) %}
76
  {% for j in range(axis + 1, rank - 1) %}
77
+ {% set ostride.value = ostride.value * outputShape[j] %}
78
  {% endfor %}
79
  {% set safeOStride = ostride.value %}
80
  {% set dstride = namespace(value=1) %}
81
  {% for j in range(axis + 1, rank) %}
82
+ {% set dstride.value = dstride.value * dataShape[j] %}
83
  {% endfor %}
84
+ {% set pre = pads[axis] %}
85
+ {% set post = pads[axis + rank] %}
86
  {% set crop = (0 - pre) if pre < 0 else 0 %}
87
  {% set trim = (0 - post) if post < 0 else 0 %}
88
+ {% set slicedRaw = dataShape[axis] - crop - trim %}
89
  {% set sliced = slicedRaw if slicedRaw > 0 else 0 %}
90
  {% set padBefore = pre if pre > 0 else 0 %}
91
  let out_coord{{ axis }} = i32(rem / {{ safeOStride }}u);
build/webgpu/pad.wgsl.jinja CHANGED
@@ -1,22 +1,20 @@
1
  {% macro flat_tail_open() %}
2
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
3
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
4
  // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
5
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills the rest into y).
6
- let invocation = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
7
  // Tail-safe scalar x4 keeps vector-like dispatch density without requiring
8
  // the logical tensor length (or its storage binding) to be vec4 aligned.
9
- let begin = invocation * {{ source.itemsPerInvocation }}u;
10
- let end = min(begin + {{ source.itemsPerInvocation }}u, params.count);
 
11
  for (var i = begin; i < end; i = i + 1u) {
12
  {%- endmacro %}
13
  {% macro flat_tail_close() %}
14
  }
15
  {% endmacro %}
16
 
17
- {% if usesF16 %}
18
- enable f16;
19
- {% endif %}
20
  {{ env.wgsl.resourceDeclarations }}
21
 
22
  fn map_coord(coord: i32, dim: i32) -> i32 {
@@ -51,18 +49,18 @@ fn map_coord(coord: i32, dim: i32) -> i32 {
51
  fn data_offset_or_invalid(out_index: u32) -> i32 {
52
  var rem = out_index;
53
  var offset = 0i;
54
- {% for axis in range(source.rank) %}
55
  {% set out_stride = namespace(value=1) %}
56
- {% for j in range(axis + 1, source.rank) %}
57
- {% set out_stride.value = out_stride.value * source.outputShape[j] %}
58
  {% endfor %}
59
  {% set out_stride_divisor = out_stride.value if out_stride.value != 0 else 1 %}
60
  {% set data_stride = namespace(value=1) %}
61
- {% for j in range(axis + 1, source.rank) %}
62
- {% set data_stride.value = data_stride.value * source.dataShape[j] %}
63
  {% endfor %}
64
- {% set pre = source.pads[axis] %}
65
- {% set post = source.pads[axis + source.rank] %}
66
  {% if pre < 0 %}
67
  {% set crop_start = 0 - pre %}
68
  {% else %}
@@ -73,7 +71,7 @@ fn data_offset_or_invalid(out_index: u32) -> i32 {
73
  {% else %}
74
  {% set crop_end_trim = 0 %}
75
  {% endif %}
76
- {% set crop_end = source.dataShape[axis] - crop_end_trim %}
77
  {% set sliced_dim = crop_end - crop_start %}
78
  {% if sliced_dim < 0 %}
79
  {% set sliced_dim = 0 %}
@@ -101,7 +99,7 @@ fn data_offset_or_invalid(out_index: u32) -> i32 {
101
  {% if valueFromInput %}
102
  output[i] = padValue[0];
103
  {% else %}
104
- output[i] = {{ scalar }}({{ source.value }});
105
  {% endif %}
106
  } else {
107
  output[i] = data[u32(src)];
 
1
  {% macro flat_tail_open() %}
2
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
3
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
4
  // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
5
+ // dispatch's per-axis workgroup fold width (the dispatch caps x and spills the rest into y).
6
+ let invocation = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
7
  // Tail-safe scalar x4 keeps vector-like dispatch density without requiring
8
  // the logical tensor length (or its storage binding) to be vec4 aligned.
9
+ {% set itemsPerInvocation = itemsPerInvocation if itemsPerInvocation is defined else 4 %}
10
+ let begin = invocation * {{ itemsPerInvocation }}u;
11
+ let end = min(begin + {{ itemsPerInvocation }}u, params.count);
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 map_coord(coord: i32, dim: i32) -> i32 {
 
49
  fn data_offset_or_invalid(out_index: u32) -> i32 {
50
  var rem = out_index;
51
  var offset = 0i;
52
+ {% for axis in range(rank) %}
53
  {% set out_stride = namespace(value=1) %}
54
+ {% for j in range(axis + 1, rank) %}
55
+ {% set out_stride.value = out_stride.value * outputShape[j] %}
56
  {% endfor %}
57
  {% set out_stride_divisor = out_stride.value if out_stride.value != 0 else 1 %}
58
  {% set data_stride = namespace(value=1) %}
59
+ {% for j in range(axis + 1, rank) %}
60
+ {% set data_stride.value = data_stride.value * dataShape[j] %}
61
  {% endfor %}
62
+ {% set pre = pads[axis] %}
63
+ {% set post = pads[axis + rank] %}
64
  {% if pre < 0 %}
65
  {% set crop_start = 0 - pre %}
66
  {% else %}
 
71
  {% else %}
72
  {% set crop_end_trim = 0 %}
73
  {% endif %}
74
+ {% set crop_end = dataShape[axis] - crop_end_trim %}
75
  {% set sliced_dim = crop_end - crop_start %}
76
  {% if sliced_dim < 0 %}
77
  {% set sliced_dim = 0 %}
 
99
  {% if valueFromInput %}
100
  output[i] = padValue[0];
101
  {% else %}
102
+ output[i] = {{ scalar }}({{ value }});
103
  {% endif %}
104
  } else {
105
  output[i] = data[u32(src)];
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Pad",
3
  "fixtureArrays": {
4
  "onnx_backend_constant_pad_input_data": [1.764052391052246, 0.40015721321105957, 0.978738009929657, 2.2408931255340576, 1.8675580024719238, -0.9772778749465942, 0.9500884413719177, -0.15135720372200012, -0.10321885347366333, 0.4105985164642334, 0.14404356479644775, 1.4542734622955322, 0.7610377073287964, 0.12167501449584961, 0.44386324286460876, 0.3336743414402008, 1.4940791130065918, -0.2051582634449005, 0.3130677044391632, -0.8540957570075989, -2.5529897212982178, 0.653618574142456, 0.8644362092018127, -0.7421650290489197, 2.269754648208618, -1.4543657302856445, 0.04575851559638977, -0.18718385696411133, 1.5327792167663574, 1.4693588018417358, 0.154947429895401, 0.37816253304481506, -0.8877857327461243, -1.980796456336975, -0.34791216254234314, 0.15634897351264954, 1.2302906513214111, 1.202379822731018, -0.38732680678367615, -0.302302747964859, -1.0485529899597168, -1.420017957687378, -1.7062702178955078, 1.950775384902954, -0.5096521973609924, -0.4380742907524109, -1.2527953386306763, 0.7774903774261475, -1.6138978004455566, -0.21274028718471527, -0.8954665660858154, 0.38690251111984253, -0.5108051300048828, -1.18063223361969, -0.02818222902715206, 0.4283318817615509, 0.06651721894741058, 0.30247190594673157, -0.6343221068382263, -0.3627411723136902]
5
  },
@@ -29,7 +28,7 @@
29
  "provenance": {
30
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
31
  "test": "PadOpTest.Pad_Constant_1D",
32
- "notes": "Uses Pad's optional `constant_value` input. Pads remain the synthesized attrs.pads initializer; the constant_value_input route consumes the `constant_value` tensor directly."
33
  },
34
  "attrs": { "mode": "constant", "pads": [1, 2] },
35
  "inputs": {
@@ -107,7 +106,7 @@
107
  "provenance": {
108
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
109
  "test": "PadOpTest.Pad_Constant_1D",
110
- "notes": "Typed ORT int32 case using Pad's optional `constant_value` input. Pads remain attrs.pads; the constant_value_input route consumes the `constant_value` tensor directly."
111
  },
112
  "attrs": { "mode": "constant", "pads": [1, 2] },
113
  "inputs": {
@@ -138,7 +137,7 @@
138
  "provenance": {
139
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
140
  "test": "PadOpTest.ConstantPadAxesOutOfOrder",
141
- "notes": "ORT supplies axes=[3,2] with pads=[1,0,1,0]; this framework represents the equivalent full-rank pads attribute."
142
  },
143
  "attrs": { "pads": [0, 0, 0, 1, 0, 0, 0, 1] },
144
  "inputs": {
@@ -165,7 +164,7 @@
165
  "provenance": {
166
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
167
  "test": "PadOpTest.ConstantPadAxes",
168
- "notes": "ORT supplies axes=[1,3] with pads=[0,1,0,1]; this framework represents the equivalent full-rank pads attribute."
169
  },
170
  "attrs": { "pads": [0, 0, 0, 1, 0, 0, 0, 1] },
171
  "inputs": {
@@ -182,7 +181,7 @@
182
  "provenance": {
183
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
184
  "test": "PadOpTest.ConstantPadAxesTest2",
185
- "notes": "ORT supplies axes=[2,3] with pads=[1,1,1,1]; this framework represents the equivalent full-rank pads attribute."
186
  },
187
  "attrs": { "pads": [0, 0, 1, 1, 0, 0, 1, 1] },
188
  "inputs": {
@@ -199,7 +198,7 @@
199
  "provenance": {
200
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
201
  "test": "PadOpTest.ConstantPadAxesTest4",
202
- "notes": "ORT ConstantPadNegativeAxes, ConstantPadAxesTest1/Test3, and ConstantPadAxesWithOneDimensionSpecified project to this same full-rank pads request."
203
  },
204
  "attrs": { "pads": [0, 0, 0, 1, 0, 0, 0, 1] },
205
  "inputs": {
@@ -343,7 +342,7 @@
343
  "provenance": {
344
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
345
  "test": "PadOpTest.Pad_Wrap_WebGpu_PadGreaterThanInputDimension",
346
- "notes": "CPU-valid upper-padding projection of ORT's WebGPU regression."
347
  },
348
  "attrs": { "pads": [0, 5], "mode": "wrap" },
349
  "inputs": {
@@ -363,7 +362,7 @@
363
  "provenance": {
364
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
365
  "test": "PadOpTest.Pad_Wrap_WebGpu_PadGreaterThanInputDimension",
366
- "notes": "Lower-padding projection of the same ORT WebGPU regression: the negative in-coordinate path of the wrap modulo."
367
  },
368
  "attrs": { "pads": [5, 0], "mode": "wrap" },
369
  "inputs": {
@@ -488,16 +487,10 @@
488
  {
489
  "name": "constant_vec4_last_axis_crop_removes_every_column",
490
  "provenance": {
491
- "notes": "A positive pad on the innermost axis and a negative one that crops away every source column: the output row is entirely pad value, so the sliced width the vec4 kernel carries is zero. That width has a floor at zero precisely for this shape and no case had ever reached it. The uniform output is the assertion -- no source value may survive."
492
  },
493
  "attrs": { "pads": [0, 8, 0, -4] },
494
- "inputs": {
495
- "data": {
496
- "dtype": "float32",
497
- "shape": [2, 4],
498
- "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.31, "scale": 2.0 }
499
- }
500
- },
501
  "outputs": { "output": { "dtype": "float32", "shape": [2, 8], "tolerance": 0 } }
502
  },
503
  {
@@ -876,7 +869,7 @@
876
  "outputs": { "output": { "dtype": "float32", "shape": [1, 3, 7, 12] } },
877
  "provenance": {
878
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_constant_pad",
879
- "notes": "ONNX int64 metadata/index tensors use framework int32/uint32 slots where representable. ONNX Pad pads/value inputs represented as framework attributes."
880
  }
881
  },
882
  {
@@ -897,7 +890,7 @@
897
  "outputs": { "output": { "dtype": "float32", "shape": [1, 3, 4, 12] } },
898
  "provenance": {
899
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_constant_pad_axes",
900
- "notes": "ONNX int64 metadata uses supported framework storage where representable, and Pad inputs are projected to the full-rank pads attribute. The official test_constant_pad_negative_axes fixture projects to this same request."
901
  }
902
  },
903
  {
@@ -916,7 +909,7 @@
916
  "outputs": { "output": { "dtype": "int32", "shape": [1, 3, 6, 7] } },
917
  "provenance": {
918
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_edge_pad",
919
- "notes": "ONNX int64 metadata/index tensors use framework int32/uint32 slots where representable. ONNX Pad pads/value inputs represented as framework attributes."
920
  }
921
  },
922
  {
@@ -935,7 +928,7 @@
935
  "outputs": { "output": { "dtype": "int32", "shape": [1, 3, 6, 7] } },
936
  "provenance": {
937
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_reflect_pad",
938
- "notes": "ONNX int64 metadata/index tensors use framework int32/uint32 slots where representable. ONNX Pad pads/value inputs represented as framework attributes."
939
  }
940
  },
941
  {
@@ -954,7 +947,7 @@
954
  "outputs": { "output": { "dtype": "int32", "shape": [1, 3, 6, 7] } },
955
  "provenance": {
956
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_wrap_pad",
957
- "notes": "ONNX int64 metadata/index tensors use framework int32/uint32 slots where representable. ONNX Pad pads/value inputs represented as framework attributes."
958
  }
959
  },
960
  {
@@ -962,7 +955,7 @@
962
  "provenance": {
963
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
964
  "test": "PadOpTest.Pad_Edge_3D",
965
- "notes": "ONNX Pad pads/value inputs represented as framework attributes."
966
  },
967
  "attrs": { "mode": "edge", "pads": [1, 2, 2, 1, 2, 2] },
968
  "inputs": {
@@ -979,7 +972,7 @@
979
  "provenance": {
980
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
981
  "test": "PadOpTest.Pad_Constant_3D_Inner_No_Padding",
982
- "notes": "ONNX Pad pads/value inputs represented as framework attributes."
983
  },
984
  "attrs": { "mode": "constant", "pads": [1, 1, 0, 1, 1, 0] },
985
  "inputs": {
@@ -993,7 +986,7 @@
993
  "provenance": {
994
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
995
  "test": "PadOpTest.Pad_Edge_3D_Inner_No_Padding",
996
- "notes": "ONNX Pad pads/value inputs represented as framework attributes."
997
  },
998
  "attrs": { "mode": "edge", "pads": [1, 1, 0, 1, 1, 0] },
999
  "inputs": {
@@ -1006,7 +999,7 @@
1006
  "provenance": {
1007
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1008
  "test": "PadOpTest.Pad_Edge_3D_Last_Pad_Slice_Inner_No_Padding",
1009
- "notes": "ONNX Pad pads/value inputs represented as framework attributes."
1010
  },
1011
  "attrs": { "mode": "edge", "pads": [1, -1, 0, 1, 1, 0] },
1012
  "inputs": {
@@ -1019,7 +1012,7 @@
1019
  "provenance": {
1020
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1021
  "test": "PadOpTest.Pad_Edge_3D_Last_Slice_Inner_No_Padding",
1022
- "notes": "ONNX Pad pads/value inputs represented as framework attributes."
1023
  },
1024
  "attrs": { "mode": "edge", "pads": [1, -1, 0, 1, 0, 0] },
1025
  "inputs": {
@@ -1032,7 +1025,7 @@
1032
  "provenance": {
1033
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1034
  "test": "PadOpTest.Pad_Reflect_3D_Last_Pad_Slice_Inner_No_Padding",
1035
- "notes": "ONNX Pad pads/value inputs represented as framework attributes."
1036
  },
1037
  "attrs": { "mode": "reflect", "pads": [1, -1, 0, 1, 1, 0] },
1038
  "inputs": {
@@ -1045,7 +1038,7 @@
1045
  "provenance": {
1046
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1047
  "test": "PadOpTest.Pad_Reflect_3D_Last_Slice_Inner_No_Padding",
1048
- "notes": "ONNX Pad pads/value inputs represented as framework attributes."
1049
  },
1050
  "attrs": { "mode": "reflect", "pads": [1, -1, 0, 1, 0, 0] },
1051
  "inputs": {
@@ -1058,7 +1051,7 @@
1058
  "provenance": {
1059
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1060
  "test": "PadOpTest.Pad_wrap_3D_Inner_No_Padding2",
1061
- "notes": "ONNX Pad pads/value inputs represented as framework attributes."
1062
  },
1063
  "attrs": { "mode": "wrap", "pads": [1, 2, 0, 1, 2, 0] },
1064
  "inputs": {
@@ -1099,7 +1092,7 @@
1099
  "provenance": {
1100
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1101
  "test": "PadOpTest.Pad_Constant_1D",
1102
- "notes": "Signed companion for exact integer padding values: -16777217 is representable as int32 but rounds to -16777216 if routed through f32."
1103
  },
1104
  "attrs": { "mode": "constant", "pads": [1, 1] },
1105
  "inputs": {
@@ -1218,7 +1211,7 @@
1218
  "provenance": {
1219
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1220
  "test": "PadOpTest.Pad_Constant_1D",
1221
- "notes": "Scalar-path companion to the aligned int32/uint32 large-attribute-value cases. The output's last dimension is 3, which excludes the vec4 path and verifies that exact int32 attribute conversion is preserved by the scalar fallback."
1222
  },
1223
  "attrs": { "mode": "constant", "pads": [1, 1] },
1224
  "inputs": {
@@ -1266,7 +1259,7 @@
1266
  "provenance": {
1267
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1268
  "test": "PadOpTest.Pad_Edge_2D",
1269
- "notes": "uint8 with a non-constant (edge) mode was untested; edge/reflect/wrap were only covered for f32/int32. Pure value-copy, exact."
1270
  },
1271
  "attrs": { "mode": "edge", "pads": [1, 2, 1, 2] },
1272
  "inputs": {
@@ -1340,7 +1333,7 @@
1340
  {
1341
  "name": "rank3_value_input_scalar_path_perf_compact",
1342
  "provenance": {
1343
- "notes": "Compact sibling for the rank-3 value-input Pad benchmark; optional `constant_value` input selects constant_value_input and the unaligned inner extent keeps scalar address handling active."
1344
  },
1345
  "attrs": { "mode": "constant", "pads": [1, 0, 2, 1, 0, 2] },
1346
  "inputs": {
 
1
  {
 
2
  "fixtureArrays": {
3
  "onnx_backend_constant_pad_input_data": [1.764052391052246, 0.40015721321105957, 0.978738009929657, 2.2408931255340576, 1.8675580024719238, -0.9772778749465942, 0.9500884413719177, -0.15135720372200012, -0.10321885347366333, 0.4105985164642334, 0.14404356479644775, 1.4542734622955322, 0.7610377073287964, 0.12167501449584961, 0.44386324286460876, 0.3336743414402008, 1.4940791130065918, -0.2051582634449005, 0.3130677044391632, -0.8540957570075989, -2.5529897212982178, 0.653618574142456, 0.8644362092018127, -0.7421650290489197, 2.269754648208618, -1.4543657302856445, 0.04575851559638977, -0.18718385696411133, 1.5327792167663574, 1.4693588018417358, 0.154947429895401, 0.37816253304481506, -0.8877857327461243, -1.980796456336975, -0.34791216254234314, 0.15634897351264954, 1.2302906513214111, 1.202379822731018, -0.38732680678367615, -0.302302747964859, -1.0485529899597168, -1.420017957687378, -1.7062702178955078, 1.950775384902954, -0.5096521973609924, -0.4380742907524109, -1.2527953386306763, 0.7774903774261475, -1.6138978004455566, -0.21274028718471527, -0.8954665660858154, 0.38690251111984253, -0.5108051300048828, -1.18063223361969, -0.02818222902715206, 0.4283318817615509, 0.06651721894741058, 0.30247190594673157, -0.6343221068382263, -0.3627411723136902]
4
  },
 
28
  "provenance": {
29
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
30
  "test": "PadOpTest.Pad_Constant_1D",
31
+ "notes": "Uses Pad's optional `constant_value` input. Pads remain supplied by the `pads` attribute, while the fill value comes directly from the input tensor."
32
  },
33
  "attrs": { "mode": "constant", "pads": [1, 2] },
34
  "inputs": {
 
106
  "provenance": {
107
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
108
  "test": "PadOpTest.Pad_Constant_1D",
109
+ "notes": "Typed ONNX Runtime int32 case using Pad's optional `constant_value` input. Pads remain supplied by the `pads` attribute, while the fill value comes directly from the input tensor."
110
  },
111
  "attrs": { "mode": "constant", "pads": [1, 2] },
112
  "inputs": {
 
137
  "provenance": {
138
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
139
  "test": "PadOpTest.ConstantPadAxesOutOfOrder",
140
+ "notes": "The axes-specific request axes=[3,2], pads=[1,0,1,0] is represented by its equivalent full-rank pads attribute."
141
  },
142
  "attrs": { "pads": [0, 0, 0, 1, 0, 0, 0, 1] },
143
  "inputs": {
 
164
  "provenance": {
165
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
166
  "test": "PadOpTest.ConstantPadAxes",
167
+ "notes": "The axes-specific request axes=[1,3], pads=[0,1,0,1] is represented by its equivalent full-rank pads attribute."
168
  },
169
  "attrs": { "pads": [0, 0, 0, 1, 0, 0, 0, 1] },
170
  "inputs": {
 
181
  "provenance": {
182
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
183
  "test": "PadOpTest.ConstantPadAxesTest2",
184
+ "notes": "The axes-specific request axes=[2,3], pads=[1,1,1,1] is represented by its equivalent full-rank pads attribute."
185
  },
186
  "attrs": { "pads": [0, 0, 1, 1, 0, 0, 1, 1] },
187
  "inputs": {
 
198
  "provenance": {
199
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
200
  "test": "PadOpTest.ConstantPadAxesTest4",
201
+ "notes": "A full-rank `pads` request exercises constant padding without an `axes` input."
202
  },
203
  "attrs": { "pads": [0, 0, 0, 1, 0, 0, 0, 1] },
204
  "inputs": {
 
342
  "provenance": {
343
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
344
  "test": "PadOpTest.Pad_Wrap_WebGpu_PadGreaterThanInputDimension",
345
+ "notes": "Upper wrap padding larger than the input length exercises repeated positive modulo mapping."
346
  },
347
  "attrs": { "pads": [0, 5], "mode": "wrap" },
348
  "inputs": {
 
362
  "provenance": {
363
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
364
  "test": "PadOpTest.Pad_Wrap_WebGpu_PadGreaterThanInputDimension",
365
+ "notes": "Lower wrap padding larger than the input length exercises negative coordinates before modulo normalization."
366
  },
367
  "attrs": { "pads": [5, 0], "mode": "wrap" },
368
  "inputs": {
 
487
  {
488
  "name": "constant_vec4_last_axis_crop_removes_every_column",
489
  "provenance": {
490
+ "notes": "A positive innermost pad plus negative cropping removes every source column. The vectorized path carries a zero sliced width, and the uniform output verifies that no source value survives."
491
  },
492
  "attrs": { "pads": [0, 8, 0, -4] },
493
+ "inputs": { "data": { "dtype": "float32", "shape": [2, 4], "data": { "kind": "constant", "value": 2.0 } } },
 
 
 
 
 
 
494
  "outputs": { "output": { "dtype": "float32", "shape": [2, 8], "tolerance": 0 } }
495
  },
496
  {
 
869
  "outputs": { "output": { "dtype": "float32", "shape": [1, 3, 7, 12] } },
870
  "provenance": {
871
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_constant_pad",
872
+ "notes": "This WebGPU package stores representable ONNX int64 metadata in int32 or uint32 slots. The fixture represents `pads` and `constant_value` inputs as attributes."
873
  }
874
  },
875
  {
 
890
  "outputs": { "output": { "dtype": "float32", "shape": [1, 3, 4, 12] } },
891
  "provenance": {
892
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_constant_pad_axes",
893
+ "notes": "This WebGPU package stores representable ONNX int64 metadata in int32 slots. The negative-axis Pad request is expanded to a full-rank `pads` attribute."
894
  }
895
  },
896
  {
 
909
  "outputs": { "output": { "dtype": "int32", "shape": [1, 3, 6, 7] } },
910
  "provenance": {
911
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_edge_pad",
912
+ "notes": "This WebGPU package stores representable ONNX int64 metadata in int32 or uint32 slots. The fixture represents `pads` and `constant_value` inputs as attributes."
913
  }
914
  },
915
  {
 
928
  "outputs": { "output": { "dtype": "int32", "shape": [1, 3, 6, 7] } },
929
  "provenance": {
930
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_reflect_pad",
931
+ "notes": "This WebGPU package stores representable ONNX int64 metadata in int32 or uint32 slots. The fixture represents `pads` and `constant_value` inputs as attributes."
932
  }
933
  },
934
  {
 
947
  "outputs": { "output": { "dtype": "int32", "shape": [1, 3, 6, 7] } },
948
  "provenance": {
949
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_wrap_pad",
950
+ "notes": "This WebGPU package stores representable ONNX int64 metadata in int32 or uint32 slots. The fixture represents `pads` and `constant_value` inputs as attributes."
951
  }
952
  },
953
  {
 
955
  "provenance": {
956
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
957
  "test": "PadOpTest.Pad_Edge_3D",
958
+ "notes": "The fixture represents the ONNX `pads` and optional `constant_value` inputs as attributes."
959
  },
960
  "attrs": { "mode": "edge", "pads": [1, 2, 2, 1, 2, 2] },
961
  "inputs": {
 
972
  "provenance": {
973
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
974
  "test": "PadOpTest.Pad_Constant_3D_Inner_No_Padding",
975
+ "notes": "The fixture represents the ONNX `pads` and optional `constant_value` inputs as attributes."
976
  },
977
  "attrs": { "mode": "constant", "pads": [1, 1, 0, 1, 1, 0] },
978
  "inputs": {
 
986
  "provenance": {
987
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
988
  "test": "PadOpTest.Pad_Edge_3D_Inner_No_Padding",
989
+ "notes": "The fixture represents the ONNX `pads` and optional `constant_value` inputs as attributes."
990
  },
991
  "attrs": { "mode": "edge", "pads": [1, 1, 0, 1, 1, 0] },
992
  "inputs": {
 
999
  "provenance": {
1000
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1001
  "test": "PadOpTest.Pad_Edge_3D_Last_Pad_Slice_Inner_No_Padding",
1002
+ "notes": "The fixture represents the ONNX `pads` and optional `constant_value` inputs as attributes."
1003
  },
1004
  "attrs": { "mode": "edge", "pads": [1, -1, 0, 1, 1, 0] },
1005
  "inputs": {
 
1012
  "provenance": {
1013
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1014
  "test": "PadOpTest.Pad_Edge_3D_Last_Slice_Inner_No_Padding",
1015
+ "notes": "The fixture represents the ONNX `pads` and optional `constant_value` inputs as attributes."
1016
  },
1017
  "attrs": { "mode": "edge", "pads": [1, -1, 0, 1, 0, 0] },
1018
  "inputs": {
 
1025
  "provenance": {
1026
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1027
  "test": "PadOpTest.Pad_Reflect_3D_Last_Pad_Slice_Inner_No_Padding",
1028
+ "notes": "The fixture represents the ONNX `pads` and optional `constant_value` inputs as attributes."
1029
  },
1030
  "attrs": { "mode": "reflect", "pads": [1, -1, 0, 1, 1, 0] },
1031
  "inputs": {
 
1038
  "provenance": {
1039
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1040
  "test": "PadOpTest.Pad_Reflect_3D_Last_Slice_Inner_No_Padding",
1041
+ "notes": "The fixture represents the ONNX `pads` and optional `constant_value` inputs as attributes."
1042
  },
1043
  "attrs": { "mode": "reflect", "pads": [1, -1, 0, 1, 0, 0] },
1044
  "inputs": {
 
1051
  "provenance": {
1052
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1053
  "test": "PadOpTest.Pad_wrap_3D_Inner_No_Padding2",
1054
+ "notes": "The fixture represents the ONNX `pads` and optional `constant_value` inputs as attributes."
1055
  },
1056
  "attrs": { "mode": "wrap", "pads": [1, 2, 0, 1, 2, 0] },
1057
  "inputs": {
 
1092
  "provenance": {
1093
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1094
  "test": "PadOpTest.Pad_Constant_1D",
1095
+ "notes": "The padding value -16,777,217 is representable as int32 but rounds to -16,777,216 if routed through f32."
1096
  },
1097
  "attrs": { "mode": "constant", "pads": [1, 1] },
1098
  "inputs": {
 
1211
  "provenance": {
1212
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1213
  "test": "PadOpTest.Pad_Constant_1D",
1214
+ "notes": "An output width of 3 exercises scalar padding and verifies exact conversion of an int32 attribute value above 2^24."
1215
  },
1216
  "attrs": { "mode": "constant", "pads": [1, 1] },
1217
  "inputs": {
 
1259
  "provenance": {
1260
  "source": "onnxruntime/test/providers/cpu/tensor/pad_test.cc",
1261
  "test": "PadOpTest.Pad_Edge_2D",
1262
+ "notes": "Uint8 edge padding is a bit-exact value-copy operation."
1263
  },
1264
  "attrs": { "mode": "edge", "pads": [1, 2, 1, 2] },
1265
  "inputs": {
 
1333
  {
1334
  "name": "rank3_value_input_scalar_path_perf_compact",
1335
  "provenance": {
1336
+ "notes": "A rank-3 Pad with an optional `constant_value` input and unaligned inner extent exercises scalar address handling."
1337
  },
1338
  "attrs": { "mode": "constant", "pads": [1, 0, 2, 1, 0, 2] },
1339
  "inputs": {