Skip to content

Commit eb457f1

Browse files
authored
Arm backend: Add missing tests/fixes for U55/U85. (pytorch#23052)
This PR adds fixes and tests, so the tolerance in the script for automatic list of supported ops is zero. The script is a proper coverage tool now. cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani Signed-off-by: Elena Zhelezina <elena.zhelezina@arm.com>
1 parent 4fd39a2 commit eb457f1

5 files changed

Lines changed: 93 additions & 4 deletions

File tree

‎backends/arm/scripts/docgen/generate_op_support.py‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class BackendConfig:
103103
# operator is unsupported. They should still count as static evidence.
104104
infrastructure_xfail_markers=frozenset({"XfailIfNoCorstone300"}),
105105
filter_u55_unsupported_ops=True,
106-
max_missing_profile_cells=4,
106+
max_missing_profile_cells=0,
107107
),
108108
"u85": BackendConfig(
109109
key="u85",
@@ -119,7 +119,7 @@ class BackendConfig:
119119
# A missing Corstone-320 FVP is infrastructure, not evidence that the
120120
# operator itself is unsupported.
121121
infrastructure_xfail_markers=frozenset({"XfailIfNoCorstone320"}),
122-
max_missing_profile_cells=3,
122+
max_missing_profile_cells=0,
123123
),
124124
}
125125

@@ -355,6 +355,7 @@ def _activate_backend(backend: str | BackendConfig) -> BackendConfig:
355355
"torch.ops.aten.native_group_norm.default",
356356
"torch.ops.aten.native_layer_norm.default",
357357
"torch.ops.aten.embedding.default",
358+
"torch.ops.aten.pow.Tensor_Tensor",
358359
}
359360

360361

@@ -421,6 +422,12 @@ def _activate_backend(backend: str | BackendConfig) -> BackendConfig:
421422
): {
422423
"INT": {"torch.ops.aten.index_select.default"},
423424
},
425+
(
426+
"backends/arm/test/ops/test_index_tensor.py",
427+
"test_index_tensor_u55_INT_constant",
428+
): {
429+
"INT": {"torch.ops.aten.index.Tensor"},
430+
},
424431
(
425432
"backends/arm/test/ops/test_silu.py",
426433
"test_silu_u55_INT",
@@ -433,11 +440,19 @@ def _activate_backend(backend: str | BackendConfig) -> BackendConfig:
433440
): {
434441
"INT": {"torch.ops.aten.unfold_copy.default"},
435442
},
443+
(
444+
"backends/arm/test/ops/test_pow.py",
445+
"test_pow_tensor_tensor_u55_INT",
446+
): {
447+
"INT": {"torch.ops.aten.pow.Tensor_Tensor"},
448+
},
436449
}
437450

438451
# Existing U85 runtime tests below intentionally suppress direct ATen/Edge
439452
# assertions because quantization/decomposition changes the graph. They still
440453
# provide positive runtime coverage for the exported operator.
454+
# The test for embedding op has pipeline.pop_stage("check.aten"),
455+
# that is why we need to set it explicitly.
441456
U85_EXPLICIT_BACKEND_COVERAGE: dict[tuple[str, str], dict[str, set[str]]] = {
442457
(
443458
"backends/arm/test/ops/test_div_tensor_mode.py",
@@ -451,6 +466,18 @@ def _activate_backend(backend: str | BackendConfig) -> BackendConfig:
451466
): {
452467
"INT": {"torch.ops.aten.silu.default"},
453468
},
469+
(
470+
"backends/arm/test/ops/test_embedding.py",
471+
"test_embedding_u85_INT",
472+
): {
473+
"INT": {"torch.ops.aten.embedding.default"},
474+
},
475+
(
476+
"backends/arm/test/ops/test_pow.py",
477+
"test_pow_tensor_tensor_u85_INT",
478+
): {
479+
"INT": {"torch.ops.aten.pow.Tensor_Tensor"},
480+
},
454481
}
455482

456483

‎backends/arm/test/ops/test_argmax.py‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import torch
99
from executorch.backends.arm.test import common
1010
from executorch.backends.arm.test.tester.test_pipeline import (
11+
EthosU55PipelineINT,
12+
EthosU85PipelineINT,
1113
OpNotSupportedPipeline,
1214
TosaPipelineFP,
1315
TosaPipelineINT,
@@ -65,6 +67,21 @@ def forward(self, x: torch.Tensor):
6567
),
6668
}
6769

70+
test_data_u55_int: dict[str, Tuple[input_t, int]] = {
71+
"rank_1_dim_0_int8": lambda: (
72+
(torch.randint(-128, 127, (10,), dtype=torch.int8),),
73+
0,
74+
),
75+
"rank_2_dim_1_int8": lambda: (
76+
(torch.randint(-128, 127, (2, 5), dtype=torch.int8),),
77+
1,
78+
),
79+
"rank_4_dim_3_int8": lambda: (
80+
(torch.randint(-128, 127, (1, 3, 4, 5), dtype=torch.int8),),
81+
3,
82+
),
83+
}
84+
6885

6986
class ArgmaxAll(torch.nn.Module):
7087
def forward(self, x: torch.Tensor):
@@ -137,6 +154,32 @@ def test_argmax_tosa_INT(test_data: Tuple[input_t, int]):
137154
pipeline.run()
138155

139156

157+
@common.parametrize("test_data", Argmax.test_data_u55_int)
158+
@common.XfailIfNoCorstone300
159+
def test_argmax_u55_INT(test_data: Tuple[input_t, int]):
160+
data, dim = test_data()
161+
pipeline = EthosU55PipelineINT[input_t](
162+
Argmax(dim),
163+
data,
164+
aten_op,
165+
[exir_op, to_copy_exir_op],
166+
)
167+
pipeline.run()
168+
169+
170+
@common.parametrize("test_data", Argmax.test_data_int)
171+
@common.XfailIfNoCorstone320
172+
def test_argmax_u85_INT(test_data: Tuple[input_t, int]):
173+
data, dim = test_data()
174+
pipeline = EthosU85PipelineINT[input_t](
175+
Argmax(dim),
176+
data,
177+
aten_op,
178+
[exir_op, to_copy_exir_op],
179+
)
180+
pipeline.run()
181+
182+
140183
@common.parametrize(
141184
"test_data",
142185
Argmax.test_data | Argmax.test_data_fp16 | Argmax.test_data_bf16,

‎backends/arm/test/ops/test_pow.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,21 @@ def test_pow_tensor_tensor_u55_INT(
356356
pipeline.run()
357357

358358

359+
@common.parametrize("test_data", Pow_TensorTensor.test_data_quant)
360+
@common.XfailIfNoCorstone320
361+
def test_pow_tensor_tensor_u85_INT(
362+
test_data: Pow_TensorTensor.input_t,
363+
):
364+
pipeline = EthosU85PipelineINT[Pow_TensorTensor.input_t](
365+
Pow_TensorTensorPositiveBase(),
366+
test_data(),
367+
POW_TENSOR_TENSOR_INT_ATEN_OPS,
368+
POW_TENSOR_TENSOR_INT_EXIR_OPS,
369+
qtol=4,
370+
)
371+
pipeline.run()
372+
373+
359374
def test_pow_tensor_tensor_int_rejects_base_lower_bound_quantized_to_zero():
360375
"""A positive FP lower bound must stay positive after LOG input QDQ."""
361376
# abs(x) + 0.25 is structurally >= 0.25, but this calibration range makes

‎docs/source/backends/arm-ethos-u/U55_op_support.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This page lists Ethos-U55-supported PyTorch APIs and the dtype and quantization
66

77
`8x8` means 8-bit activations and 8-bit weights. `16x8` means 16-bit activations and 8-bit weights. `8x4` means 8-bit activations and 4-bit weights.
88

9-
Total supported PyTorch APIs: **101**.
9+
Total supported PyTorch APIs: **103**.
1010

1111
| PyTorch API | Support profile | DType | Quantization mode |
1212
| --- | --- | --- | --- |
@@ -18,6 +18,7 @@ Total supported PyTorch APIs: **101**.
1818
| `torch.amax` | INT | `INT8` | 8x8 |
1919
| `torch.amin` | INT | `INT8` | 8x8 |
2020
| `torch.arange` | INT | `INT8` | 8x8 |
21+
| `torch.argmax` | INT | `INT8` | 8x8 |
2122
| `torch.as_strided_copy` | INT | `INT8` | 8x8 |
2223
| `torch.asin` | INT | `INT8` | 8x8 |
2324
| `torch.asinh` | INT | `INT8` | 8x8 |
@@ -101,6 +102,7 @@ Total supported PyTorch APIs: **101**.
101102
| `torch.t` / `torch.Tensor.t` | INT | `INT8` | 8x8 |
102103
| `torch.tan` | INT | `INT8` | 8x8 |
103104
| `torch.tanh` / `torch.nn.Tanh` | INT | `INT8`, `INT16` | 8x8, 16x8 |
105+
| `torch.Tensor.__getitem__` / `tensor indexing` | INT | `INT8` | 8x8 |
104106
| `torch.Tensor.__getitem__` / `tensor slicing` | INT | `INT8`, `INT16` | 8x8, 16x8 |
105107
| `torch.Tensor.copy_` | INT | `INT8` | 8x8 |
106108
| `torch.Tensor.expand` | INT | `INT8` | 8x8 |

‎docs/source/backends/arm-ethos-u/U85_op_support.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This page lists Ethos-U85-supported PyTorch APIs and the dtype and quantization
66

77
`8x8` means 8-bit activations and 8-bit weights. `16x8` means 16-bit activations and 8-bit weights. `8x4` means 8-bit activations and 4-bit weights.
88

9-
Total supported PyTorch APIs: **124**.
9+
Total supported PyTorch APIs: **126**.
1010

1111
| PyTorch API | Support profile | DType | Quantization mode |
1212
| --- | --- | --- | --- |
@@ -20,6 +20,7 @@ Total supported PyTorch APIs: **124**.
2020
| `torch.amin` | INT | `INT8`, `INT16` | 8x8, 16x8 |
2121
| `torch.any` | INT | `INT8` | 8x8 |
2222
| `torch.arange` | INT | `INT8` | 8x8 |
23+
| `torch.argmax` | INT | `INT8` | 8x8 |
2324
| `torch.as_strided_copy` | INT | `INT8` | 8x8 |
2425
| `torch.asin` | INT | `INT8` | 8x8 |
2526
| `torch.asinh` | INT | `INT8` | 8x8 |
@@ -84,6 +85,7 @@ Total supported PyTorch APIs: **124**.
8485
| `torch.nn.AvgPool2d` / `torch.nn.functional.avg_pool2d` | INT | `INT8`, `INT16` | 8x8, 16x8 |
8586
| `torch.nn.Conv2d` / `torch.nn.functional.conv2d` | INT | `INT8`, `INT16`, `INT4` | 8x8, 8x4, 16x8 |
8687
| `torch.nn.ELU` / `torch.nn.functional.elu` | INT | `INT8` | 8x8 |
88+
| `torch.nn.Embedding` / `torch.nn.functional.embedding` | INT | `INT8` | 8x8 |
8789
| `torch.nn.functional.pad` | INT | `INT8` | 8x8 |
8890
| `torch.nn.GELU` / `torch.nn.functional.gelu` | INT | `INT8`, `INT16` | 8x8, 16x8 |
8991
| `torch.nn.Hardsigmoid` / `torch.nn.functional.hardsigmoid` | INT | `INT8` | 8x8 |

0 commit comments

Comments
 (0)