Skip to content

Commit 5129545

Browse files
authored
Arm backend: Add TOSA dialect ARGMAX op (pytorch#20112)
Added ARGMAX tosa dialect op Signed-off-by: Saoirse Stewart <saoirse.stewart@arm.com>
1 parent 630ddba commit 5129545

3 files changed

Lines changed: 122 additions & 0 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright 2026 Arm Limited and/or its affiliates.
2+
#
3+
# This source code is licensed under the BSD-style license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
import executorch.backends.arm.tosa.dialect # noqa: F401
7+
import pytest
8+
import torch
9+
from executorch.backends.arm.tosa.dialect.lib import TosaValueError
10+
from executorch.backends.arm.tosa.specification import (
11+
TosaLoweringContext,
12+
TosaSpecification,
13+
)
14+
from executorch.exir.dialects._ops import ops as exir_ops
15+
from torch._subclasses.fake_tensor import FakeTensorMode
16+
17+
18+
def test_argmax_tosa_fp() -> None:
19+
sample_input = torch.randn((2, 3, 4), dtype=torch.float32)
20+
21+
with TosaLoweringContext(
22+
TosaSpecification.create_from_string("TOSA-1.1+FP")
23+
), FakeTensorMode() as mode:
24+
output = exir_ops.backend.tosa.ARGMAX.default(
25+
mode.from_tensor(sample_input),
26+
axis=1,
27+
)
28+
29+
assert output.dtype == torch.int32
30+
assert tuple(output.shape) == (2, 4)
31+
32+
33+
def test_argmax_rejects_bfloat16_without_extension() -> None:
34+
sample_input = torch.randn((2, 3, 4), dtype=torch.bfloat16)
35+
36+
with TosaLoweringContext(
37+
TosaSpecification.create_from_string("TOSA-1.1+FP")
38+
), FakeTensorMode() as mode:
39+
with pytest.raises(TosaValueError, match="doesn't support bfloat16"):
40+
exir_ops.backend.tosa.ARGMAX.default(
41+
mode.from_tensor(sample_input),
42+
axis=1,
43+
)

backends/arm/tosa/dialect/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from executorch.backends.arm.tosa.dialect.ops import ( # noqa F401
77
activation,
8+
argmax,
89
avg_pool2d,
910
avg_pool2d_adaptive,
1011
conv2d,
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright 2026 Arm Limited and/or its affiliates.
2+
#
3+
# This source code is licensed under the BSD-style license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
import torch
7+
from executorch.backends.arm.tosa.dialect.lib import TosaValueError
8+
from executorch.backends.arm.tosa.dialect.ops._common import validate_nan_mode
9+
from executorch.backends.arm.tosa.dialect.ops_registration import register_fake_tosa_op
10+
from executorch.backends.arm.tosa.specification import (
11+
get_context_spec,
12+
TosaSpecification,
13+
)
14+
15+
16+
def _validate_argmax_dtype(dtype: torch.dtype) -> None:
17+
tosa_spec = get_context_spec()
18+
19+
if dtype == torch.int8:
20+
if not tosa_spec.support_integer():
21+
raise TosaValueError(
22+
f"TOSA spec {tosa_spec} doesn't support int8 for ARGMAX",
23+
op="ARGMAX",
24+
)
25+
return
26+
27+
if dtype == torch.int16:
28+
if not (tosa_spec.support_integer() and tosa_spec.support_extension("int16")):
29+
raise TosaValueError(
30+
f"TOSA spec {tosa_spec} doesn't support int16 for ARGMAX",
31+
op="ARGMAX",
32+
)
33+
return
34+
35+
if dtype in (torch.float16, torch.float32):
36+
if not tosa_spec.support_float():
37+
raise TosaValueError(
38+
f"TOSA spec {tosa_spec} doesn't support {dtype} for ARGMAX",
39+
op="ARGMAX",
40+
)
41+
return
42+
43+
if dtype == torch.bfloat16:
44+
if not (tosa_spec.support_float() and tosa_spec.support_extension("bf16")):
45+
raise TosaValueError(
46+
f"TOSA spec {tosa_spec} doesn't support bfloat16 for ARGMAX",
47+
op="ARGMAX",
48+
)
49+
return
50+
51+
raise TosaValueError(f"Unsupported dtype {dtype} for ARGMAX", op="ARGMAX")
52+
53+
54+
@register_fake_tosa_op(
55+
'ARGMAX(Tensor input, int axis, *, str nan_mode="PROPAGATE") -> Tensor',
56+
TosaSpecification.all_versions_and_profiles(),
57+
)
58+
def ARGMAX(
59+
input: torch.Tensor,
60+
axis: int,
61+
*,
62+
nan_mode: str = "PROPAGATE",
63+
) -> torch.Tensor:
64+
validate_nan_mode(nan_mode, "ARGMAX")
65+
_validate_argmax_dtype(input.dtype)
66+
67+
if input.dim() == 0:
68+
raise TosaValueError(
69+
"ARGMAX requires an input with rank at least 1", op="ARGMAX"
70+
)
71+
if axis < 0 or axis >= input.dim():
72+
raise TosaValueError(
73+
f"axis must be in [0, {input.dim() - 1}] but got {axis}",
74+
op="ARGMAX",
75+
)
76+
77+
output_shape = tuple(input.shape[:axis]) + tuple(input.shape[axis + 1 :])
78+
return torch.empty(output_shape, dtype=torch.int32)

0 commit comments

Comments
 (0)