Xenova HF Staff commited on
Commit
11ade4e
·
verified ·
1 Parent(s): 0a573b4

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,15 +18,15 @@ See the [ONNX `Abs` spec](https://onnx.ai/onnx/operators/onnx__Abs.html) for the
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
- | `X` | `x` | `T` | — | — | Values whose elementwise absolute values are returned. | required |
24
 
25
  ## Outputs
26
 
27
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
28
  | --- | --- | --- | --- | --- | --- | --- |
29
- | `Y` | `y` | `T` | same as `X` | same as `X` | Output tensor with elementwise absolute values. | required |
30
 
31
  ## Type constraints
32
 
@@ -36,7 +36,7 @@ See the [ONNX `Abs` spec](https://onnx.ai/onnx/operators/onnx__Abs.html) for the
36
 
37
  ## Files
38
 
39
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
40
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
41
  - [`test.json`](build/webgpu/test.json) — correctness cases
42
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -45,10 +45,14 @@ See the [ONNX `Abs` spec](https://onnx.ai/onnx/operators/onnx__Abs.html) for the
45
 
46
  ## Use with `@huggingface/kernels`
47
 
48
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
49
- It then allocates the result tensors automatically.
 
 
 
50
 
51
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
52
 
53
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
54
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
+ | `x` | `X` | `T` | — | — | Values whose elementwise absolute values are returned. | required |
24
 
25
  ## Outputs
26
 
27
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
28
  | --- | --- | --- | --- | --- | --- | --- |
29
+ | `y` | `Y` | `T` | same as `x` | same as `x` | Output tensor with elementwise absolute values. | required |
30
 
31
  ## Type constraints
32
 
 
36
 
37
  ## Files
38
 
39
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
40
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
41
  - [`test.json`](build/webgpu/test.json) — correctness cases
42
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
45
 
46
  ## Use with `@huggingface/kernels`
47
 
48
+ ```sh
49
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
50
+ ```
51
+
52
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
53
 
54
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
55
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
56
 
57
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
58
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Abs",
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,73 +2,115 @@
2
  "domain": "ai.onnx",
3
  "name": "Abs",
4
  "sinceVersion": 13,
5
- "description": "Computes the elementwise absolute value of the input tensor: `y = abs(x)`. The output has the same shape and type as the input.",
6
- "inputs": [{ "role": "X", "dtype": "T", "description": "Values whose elementwise absolute values are returned." }],
7
- "outputs": [
8
- {
9
- "role": "Y",
10
- "dtype": "T",
11
- "rank": "ranks.X",
12
- "description": "Output tensor with elementwise absolute values.",
13
- "shape": "shapes.X"
14
- }
15
- ],
16
  "typeConstraints": { "T": ["float32", "float16", "int32", "uint32", "int8", "uint8"] },
17
- "args": {
18
- "x": { "kind": "tensor", "semantic": "X", "role": "input" },
19
- "y": { "kind": "tensor", "semantic": "Y", "role": "output" }
20
- },
21
- "tunables": { "WORKGROUP_SIZE": 256 },
22
- "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"", "isInt8": "tensorDtypes.X == \"int8\"" },
23
  "variants": [
24
  {
25
  "id": "same_layout_vec4",
26
- "when": ["numel(shapes.X) > 0", "numel(shapes.X) % 4 == 0", "numel(shapes.X) == numel(shapes.Y)", "f16Ok(dtypes.T)"],
27
- "constants": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
 
28
  "passes": [
29
  {
30
  "id": "main",
31
  "name": "Abs.vec4",
32
- "source": { "shader": "unary-vec4.wgsl.jinja", "inputs": { "op": "\"abs\"" } },
 
 
 
 
33
  "bindings": [
34
- { "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$vectorScalar" },
35
- { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$vectorScalar" },
36
- {
37
- "name": "params",
38
- "semantic": "kernel.params",
39
- "buffer": { "type": "uniform" },
40
- "struct": {
41
- "name": "Params",
42
- "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }]
43
- }
44
- }
45
  ],
46
- "dispatch": { "threads": "numel(shapes.Y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
- ],
49
- "priority": 20
50
  },
51
  {
52
  "id": "elementwise",
53
- "when": "numel(shapes.X) == numel(shapes.Y) and (f16Ok(dtypes.T))",
54
  "passes": [
55
  {
56
  "id": "main",
57
  "name": "Abs",
58
- "source": { "shader": "unary-scalar.wgsl.jinja", "inputs": { "op": "\"abs\"", "itemsPerInvocation": 4 } },
 
59
  "bindings": [
60
- { "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
61
- { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
62
- {
63
- "name": "params",
64
- "semantic": "kernel.params",
65
- "buffer": { "type": "uniform" },
66
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
67
- }
68
  ],
69
- "dispatch": { "threads": "ceilDiv(numel(shapes.Y), 4)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
70
  }
71
  ]
72
  }
73
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
 
2
  "domain": "ai.onnx",
3
  "name": "Abs",
4
  "sinceVersion": 13,
5
+ "inputs": { "x": { "onnx": "X", "dtype": "T" } },
6
+ "outputs": { "y": { "onnx": "Y", "dtype": "T", "rank": "ranks.x", "shape": "shapes.x" } },
 
 
 
 
 
 
 
 
 
7
  "typeConstraints": { "T": ["float32", "float16", "int32", "uint32", "int8", "uint8"] },
8
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 } },
9
+ "derive": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"", "isInt8": "tensorDtypes.x == \"int8\"" },
 
 
 
 
10
  "variants": [
11
  {
12
  "id": "same_layout_vec4",
13
+ "priority": 20,
14
+ "when": ["numel(shapes.x) > 0", "numel(shapes.x) % 4 == 0", "numel(shapes.x) == numel(shapes.y)", "f16Ok(dtypes.T)"],
15
+ "derive": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
16
  "passes": [
17
  {
18
  "id": "main",
19
  "name": "Abs.vec4",
20
+ "shader": "unary-vec4.wgsl.jinja",
21
+ "derive": {
22
+ "op": "\"abs\"",
23
+ "vec4PerThread": "4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1"
24
+ },
25
  "bindings": [
26
+ { "arg": "x", "elementType": "$vectorScalar" },
27
+ { "arg": "y", "elementType": "$vectorScalar" },
28
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }] }
 
 
 
 
 
 
 
 
29
  ],
30
+ "dispatch": {
31
+ "x": "min(ceilDiv((ceilDiv(numel(shapes.y) / 4, 4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1)), (tunables.WORKGROUP_SIZE)), 65535)",
32
+ "y": "ceilDiv(ceilDiv((ceilDiv(numel(shapes.y) / 4, 4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1)), (tunables.WORKGROUP_SIZE)), 65535)",
33
+ "z": 1
34
+ }
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "id": "same_layout_vec4_tail",
40
+ "priority": 19,
41
+ "when": ["numel(shapes.x) > 4", "numel(shapes.x) % 4 != 0", "numel(shapes.x) == numel(shapes.y)", "f16Ok(dtypes.T)"],
42
+ "derive": {
43
+ "scalar": "dtypes.T",
44
+ "usesF16": "dtypes.T == \"f16\"",
45
+ "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\""
46
+ },
47
+ "passes": [
48
+ {
49
+ "id": "bulk",
50
+ "name": "Abs.vec4Bulk",
51
+ "shader": "unary-vec4.wgsl.jinja",
52
+ "derive": {
53
+ "op": "\"abs\"",
54
+ "vec4PerThread": "4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1"
55
+ },
56
+ "bindings": ["x", "y", "params_unary_tail"],
57
+ "dispatch": {
58
+ "x": "min(ceilDiv((ceilDiv(floor(numel(shapes.y) / 4), 4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1)), (tunables.WORKGROUP_SIZE)), 65535)",
59
+ "y": "ceilDiv(ceilDiv((ceilDiv(floor(numel(shapes.y) / 4), 4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1)), (tunables.WORKGROUP_SIZE)), 65535)",
60
+ "z": 1
61
+ }
62
+ },
63
+ {
64
+ "id": "tail",
65
+ "name": "Abs.tail",
66
+ "shader": "unary-scalar.wgsl.jinja",
67
+ "derive": { "op": "\"abs\"", "itemsPerInvocation": 4, "tailOnly": true },
68
+ "bindings": ["x_2", "y_2", "params_2_unary_tail"],
69
+ "dispatch": {
70
+ "x": "min(ceilDiv((1), (tunables.WORKGROUP_SIZE)), 65535)",
71
+ "y": "ceilDiv(ceilDiv((1), (tunables.WORKGROUP_SIZE)), 65535)",
72
+ "z": 1
73
+ }
74
  }
75
+ ]
 
76
  },
77
  {
78
  "id": "elementwise",
79
+ "when": ["numel(shapes.x) == numel(shapes.y) and (f16Ok(dtypes.T))"],
80
  "passes": [
81
  {
82
  "id": "main",
83
  "name": "Abs",
84
+ "shader": "unary-scalar.wgsl.jinja",
85
+ "derive": { "op": "\"abs\"", "itemsPerInvocation": 4 },
86
  "bindings": [
87
+ { "arg": "x" },
88
+ { "arg": "y" },
89
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
 
 
 
 
 
90
  ],
91
+ "dispatch": {
92
+ "x": "min(ceilDiv((ceilDiv(numel(shapes.y), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
93
+ "y": "ceilDiv(ceilDiv((ceilDiv(numel(shapes.y), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
94
+ "z": 1
95
+ }
96
  }
97
  ]
98
  }
99
+ ],
100
+ "bindings": {
101
+ "x": { "buffer": "read-only-storage", "elementType": "$vectorScalar" },
102
+ "y": { "buffer": "storage", "elementType": "$vectorScalar" },
103
+ "params_unary_tail": {
104
+ "buffer": "uniform",
105
+ "struct": [{ "name": "count", "type": "u32", "value": "floor(numel(shapes.y) / 4)" }],
106
+ "name": "params"
107
+ },
108
+ "x_2": { "buffer": "read-only-storage", "name": "x", "elementType": "$scalar" },
109
+ "y_2": { "buffer": "storage", "name": "y", "elementType": "$scalar" },
110
+ "params_2_unary_tail": {
111
+ "buffer": "uniform",
112
+ "name": "params",
113
+ "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }]
114
+ }
115
+ }
116
  }
build/webgpu/metadata.json CHANGED
@@ -1,19 +1,26 @@
1
  {
2
  "name": "ai.onnx.Abs",
3
- "id": "_ai_onnx_abs_webgpu_ddcdb51",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "7RY2wXX+G2yUYw7y2gr2T4300/K5hBBqB0FE2gimcak=",
11
- "manifest.json": "MQsmBQ70AVhGR52CGg4zBPCyZmebrOApnjkJg843FLU=",
12
- "test.json": "Sdtj8Gl+KH/Cw7lUkjD+zKCQvmFE8OQ0ghB/4QJ/eHw=",
13
- "unary-scalar.wgsl.jinja": "kyzzMU183om8LYcENEDXOjl8BeACsy3gej10Yr+5BCU=",
14
- "unary-vec4.wgsl.jinja": "VhBfvAxNOdJyui5Df0qqwS8240tkDRPbtPWWfzAl0pk="
15
  }
16
  },
17
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
18
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Abs" }
 
 
 
 
 
 
 
19
  }
 
1
  {
2
  "name": "ai.onnx.Abs",
3
+ "id": "_ai_onnx_abs_webgpu_d58a189",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "Ih1NAfwzMaCcTx87u2cQkpumJrkFlhSFUWTLMJKRlv8=",
11
+ "manifest.json": "ydWYMT92F2Ji3aEFK746rb5M+PZGZA5Z3KrvtTnMVb8=",
12
+ "test.json": "c5cMh22zfZDCfTTdEBgUb79TeUUQ7YS/oIvSzjP78J8=",
13
+ "unary-scalar.wgsl.jinja": "6GB3YhDxyYXLECp5KVTt8/fYEvXP7/M5CkKuLQJATgc=",
14
+ "unary-vec4.wgsl.jinja": "SNF1L8DMgZZ81K7jiPxDX62hNrEexAvUq5Lmt0uUoS8="
15
  }
16
  },
17
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
18
+ "webgpu": {
19
+ "manifestSpec": "2.0",
20
+ "variants": {
21
+ "same_layout_vec4": ["unary-vec4.wgsl.jinja"],
22
+ "same_layout_vec4_tail": ["unary-scalar.wgsl.jinja", "unary-vec4.wgsl.jinja"],
23
+ "elementwise": ["unary-scalar.wgsl.jinja"]
24
+ }
25
+ }
26
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Abs",
3
  "fixtureArrays": {
4
  "onnx_backend_input_x": [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
  },
@@ -48,7 +47,7 @@
48
  "provenance": {
49
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
50
  "test": "MathOpTest.Abs_int32",
51
- "notes": "Extends ORT's int32 Abs coverage with INT32_MIN, whose mathematical absolute value is not representable in int32 storage."
52
  },
53
  "inputs": {
54
  "x": {
@@ -64,7 +63,7 @@
64
  "provenance": {
65
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
66
  "test": "MathOpTest.Abs_int8",
67
- "notes": "Extends ORT's int8 Abs coverage with INT8_MIN, whose mathematical absolute value is not representable in int8 storage."
68
  },
69
  "inputs": { "x": { "dtype": "int8", "shape": [4], "data": { "kind": "values", "values": [-128, -127, -1, 0] } } },
70
  "outputs": { "y": { "dtype": "int8", "shape": [4], "tolerance": 0 } }
@@ -73,12 +72,12 @@
73
  "name": "ort_int16_min_value_overflow_edge_gpu_gap",
74
  "skipGpu": {
75
  "category": "todo",
76
- "reason": "int16 is widened to i32 on WebGPU, but the current Abs kernel does not narrow the result back to signed 16-bit; abs(-32768) therefore produces 32768 instead of the required wrapped int16 result."
77
  },
78
  "provenance": {
79
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
80
  "test": "MathOpTest.Abs_int32",
81
- "notes": "Signed integer Abs minimum-value edge adapted to ONNX-valid int16; ORT CPU returns the wrapped INT16_MIN value and current WebGPU Abs variants do not admit int16 tensors."
82
  },
83
  "inputs": {
84
  "x": { "dtype": "int16", "shape": [4], "data": { "kind": "values", "values": [-32768, -32767, -1, 0] } }
@@ -117,7 +116,7 @@
117
  "provenance": {
118
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
119
  "test": "MathOpTest.Abs",
120
- "notes": "Scalar-path companion for signed subnormal Abs magnitude preservation."
121
  },
122
  "inputs": {
123
  "x": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-1e-40, 0.0, 1e-40] } }
 
1
  {
 
2
  "fixtureArrays": {
3
  "onnx_backend_input_x": [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
  },
 
47
  "provenance": {
48
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
49
  "test": "MathOpTest.Abs_int32",
50
+ "notes": "INT32_MIN exercises the overflow edge where the mathematical absolute value is not representable in int32 storage."
51
  },
52
  "inputs": {
53
  "x": {
 
63
  "provenance": {
64
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
65
  "test": "MathOpTest.Abs_int8",
66
+ "notes": "INT8_MIN exercises the overflow edge where the mathematical absolute value is not representable in int8 storage."
67
  },
68
  "inputs": { "x": { "dtype": "int8", "shape": [4], "data": { "kind": "values", "values": [-128, -127, -1, 0] } } },
69
  "outputs": { "y": { "dtype": "int8", "shape": [4], "tolerance": 0 } }
 
72
  "name": "ort_int16_min_value_overflow_edge_gpu_gap",
73
  "skipGpu": {
74
  "category": "todo",
75
+ "reason": "The WebGPU path widens int16 to int32 without narrowing the Abs result back to signed 16-bit; abs(-32768) therefore produces 32768 instead of the required wrapped int16 value."
76
  },
77
  "provenance": {
78
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
79
  "test": "MathOpTest.Abs_int32",
80
+ "notes": "For ONNX int16 Abs, the minimum signed value must wrap to INT16_MIN because its positive magnitude is not representable."
81
  },
82
  "inputs": {
83
  "x": { "dtype": "int16", "shape": [4], "data": { "kind": "values", "values": [-32768, -32767, -1, 0] } }
 
116
  "provenance": {
117
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
118
  "test": "MathOpTest.Abs",
119
+ "notes": "An unaligned scalar-path input must preserve the magnitude of signed subnormal values."
120
  },
121
  "inputs": {
122
  "x": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-1e-40, 0.0, 1e-40] } }
build/webgpu/unary-scalar.wgsl.jinja CHANGED
@@ -1,21 +1,33 @@
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
- // Scalar unary fallback. Each branch retains the operation's numeric hardening,
18
- // including Payne-Hanek trigonometric range reduction and NaN/overflow guards.
 
19
  {% if usesF16 %}
20
  enable f16;
21
  {% endif %}
 
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
+ {% if tailOnly is defined and tailOnly %}
11
+ // Tail of a vec4 bulk pass: lane zero alone covers the elements the packed
12
+ // pass left behind, from the last multiple of the item count to the end.
13
+ if (invocation != 0u) {
14
+ return;
15
+ }
16
+ let begin = params.count - params.count % {{ itemsPerInvocation }}u;
17
+ let end = params.count;
18
+ {% else %}
19
+ let begin = invocation * {{ itemsPerInvocation }}u;
20
+ let end = min(begin + {{ itemsPerInvocation }}u, params.count);
21
+ {% endif %}
22
  for (var i = begin; i < end; i = i + 1u) {
23
  {%- endmacro %}
24
  {% macro flat_tail_close() %}
25
  }
26
  {% endmacro %}
27
 
28
+ {% set isInt8 = isInt8 is defined and isInt8 %}
29
+ // Scalar unary elementwise implementation. Specialization emits only the
30
+ // selected operation and any numerical helper it requires.
31
  {% if usesF16 %}
32
  enable f16;
33
  {% endif %}
build/webgpu/unary-vec4.wgsl.jinja CHANGED
@@ -1,19 +1,36 @@
1
- // Loads and stores vec4<T> (128 bits) while retaining scalar per-component
2
- // arithmetic, including per-component helper calls for guard-heavy operations.
3
- {% if usesF16 %}
4
- enable f16;
5
- {% endif %}
6
  {{ env.wgsl.resourceDeclarations }}
7
 
8
 
 
 
 
 
 
9
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
10
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
11
  // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
12
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills the rest into y).
13
- let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
 
 
 
 
 
 
 
 
 
 
 
 
14
  if (i >= params.count) {
15
  return;
16
  }
 
 
17
  let xv = x[i];
18
  {% if scalar == "i32" %}
19
  let absv = select(xv, -xv, xv < vec4<i32>(0i));
@@ -29,4 +46,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
29
  {% else %}
30
  y[i] = {{ vectorScalar }}(abs(vec4<f32>(xv)));
31
  {% endif %}
 
 
 
32
  }
 
1
+ {% set isInt8 = isInt8 is defined and isInt8 %}
2
+ // Loads and stores vec4<T> while evaluating the selected unary operation per
3
+ // component.
 
 
4
  {{ env.wgsl.resourceDeclarations }}
5
 
6
 
7
+ {% set vec4PerThread = vec4PerThread %}
8
+ {% if vec4PerThread > 1 %}
9
+ const ITEMS: u32 = {{ vec4PerThread }}u;
10
+ {% endif %}
11
+
12
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
13
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
14
  // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
15
+ // per-axis dispatch fold width (the dispatch caps x and spills the rest into y).
16
+ {% if vec4PerThread > 1 %}
17
+ // Each invocation walks ITEMS vec4 groups a span apart. Consecutive lanes
18
+ // access consecutive words on every step, while each lane can keep several
19
+ // independent loads in flight.
20
+ let tid = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
21
+ let span = (params.count + ITEMS - 1u) / ITEMS;
22
+ for (var j = 0u; j < ITEMS; j = j + 1u) {
23
+ let i = tid + j * span;
24
+ if (i >= params.count) {
25
+ break;
26
+ }
27
+ {% else %}
28
+ let i = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
29
  if (i >= params.count) {
30
  return;
31
  }
32
+ {% endif %}
33
+
34
  let xv = x[i];
35
  {% if scalar == "i32" %}
36
  let absv = select(xv, -xv, xv < vec4<i32>(0i));
 
46
  {% else %}
47
  y[i] = {{ vectorScalar }}(abs(vec4<f32>(xv)));
48
  {% endif %}
49
+ {% if vec4PerThread > 1 %}
50
+ }
51
+ {% endif %}
52
  }