Skip to content

Commit 1883128

Browse files
authored
Arm Backend: Add support for bitwise_not.default (#14460)
Adds support for the bitwise_not.default operator in the INT pipeline. Signed-off-by: Agrima Khare <[email protected]>
1 parent bb81136 commit 1883128

File tree

5 files changed

+182
-1
lines changed

5 files changed

+182
-1
lines changed

backends/arm/operator_support/ethos_u55_support.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class EthosU55NotSupported(OperatorSupportBase):
128128
exir_ops.edge.aten.bitwise_and.Scalar,
129129
exir_ops.edge.aten.bitwise_or.Scalar,
130130
exir_ops.edge.aten.bitwise_xor.Scalar,
131-
exir_ops.edge.aten.bitwise_not,
131+
exir_ops.edge.aten.bitwise_not.default,
132132
exir_ops.edge.aten.logical_and.default,
133133
exir_ops.edge.aten.logical_or.default,
134134
exir_ops.edge.aten.logical_xor.default,

backends/arm/operator_support/tosa_profile_supported_op_lists.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
exir_ops.edge.aten.logit.default,
136136
exir_ops.edge.aten.acos.default,
137137
exir_ops.edge.aten.elu.default,
138+
exir_ops.edge.aten.bitwise_not.default,
138139
}
139140

140141

backends/arm/operators/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
op_amin,
1414
op_any,
1515
op_avg_pool2d,
16+
op_bitwise_not,
1617
op_bmm,
1718
op_cat,
1819
op_ceil,
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright 2025 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+
from typing import Any, List
7+
8+
from executorch.backends.arm.operators.node_visitor import (
9+
NodeVisitor,
10+
register_node_visitor,
11+
)
12+
from executorch.backends.arm.operators.operator_validation_utils import (
13+
validate_num_inputs,
14+
validate_same_dtype,
15+
validate_valid_dtype,
16+
)
17+
from executorch.backends.arm.tosa.mapping import TosaArg
18+
from executorch.backends.arm.tosa.specification import TosaSpecification
19+
from torch.fx import Node
20+
21+
22+
@register_node_visitor
23+
class BitwiseNotVisitor(NodeVisitor):
24+
target = "aten.bitwise_not.default"
25+
26+
# bitwise_not is not supported on the FP profile
27+
tosa_specs = [
28+
TosaSpecification.create_from_string("TOSA-1.0+INT"),
29+
]
30+
31+
def __init__(self, *args):
32+
super().__init__(*args)
33+
34+
def define_node(
35+
self,
36+
node: Node,
37+
tosa_graph: Any,
38+
inputs: List[TosaArg],
39+
output: TosaArg,
40+
) -> None:
41+
42+
import serializer.tosa_serializer as ts # type: ignore
43+
44+
validate_num_inputs(self.target, inputs, 1)
45+
validate_same_dtype(self.target, [*inputs, output], ts)
46+
validate_valid_dtype(
47+
self.target,
48+
[*inputs, output],
49+
[ts.DType.INT8, ts.DType.INT16, ts.DType.INT32],
50+
output.tosa_spec,
51+
)
52+
53+
self._serialize_operator(
54+
node,
55+
tosa_graph,
56+
ts.TosaOp.Op().BITWISE_NOT,
57+
[inputs[0].name],
58+
[output.name],
59+
)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Copyright 2025 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+
from typing import Tuple
7+
8+
import torch
9+
10+
from executorch.backends.arm.test import common
11+
from executorch.backends.arm.test.tester.test_pipeline import (
12+
EthosU85PipelineINT,
13+
OpNotSupportedPipeline,
14+
TosaPipelineINT,
15+
VgfPipeline,
16+
)
17+
18+
aten_op = "torch.ops.aten.bitwise_not.default"
19+
exir_op = "executorch_exir_dialects_edge__ops_aten_bitwise_not_default"
20+
21+
input_t1 = Tuple[torch.Tensor]
22+
23+
test_data_suite = {
24+
"zeros": torch.zeros(1, 10, 10, 10, dtype=torch.int32),
25+
"ones": torch.ones(10, 2, 3, dtype=torch.int8),
26+
"pattern1_int8": 0xAA * torch.ones(1, 2, 2, 2, dtype=torch.int8),
27+
"pattern1_int16": 0xAAAA * torch.ones(1, 2, 2, 2, dtype=torch.int16),
28+
"pattern1_int32": 0xAAAAAAAA * torch.ones(1, 2, 2, 2, dtype=torch.int32),
29+
"pattern2_int8": 0xCC * torch.ones(1, 2, 2, 2, dtype=torch.int8),
30+
"pattern2_int16": 0xCCCC * torch.ones(1, 2, 2, 2, dtype=torch.int16),
31+
"pattern2_int32": 0xCCCCCCCC * torch.ones(1, 2, 2, 2, dtype=torch.int32),
32+
"rand_rank2": torch.randint(-128, 127, (10, 10), dtype=torch.int8),
33+
"rand_rank4": torch.randint(-128, 127, (1, 10, 10, 10), dtype=torch.int8),
34+
}
35+
36+
37+
class BitwiseNot(torch.nn.Module):
38+
39+
def forward(self, x: torch.Tensor):
40+
return torch.bitwise_not(x)
41+
42+
43+
@common.parametrize("test_data", test_data_suite)
44+
def test_bitwise_not_tosa_FP(test_data: Tuple):
45+
# We don't delegate bitwise_not since it is not supported on the FP profile.
46+
pipeline = OpNotSupportedPipeline[input_t1](
47+
BitwiseNot(),
48+
(test_data,),
49+
{exir_op: 1},
50+
quantize=False,
51+
)
52+
pipeline.run()
53+
54+
55+
@common.parametrize("test_data", test_data_suite)
56+
def test_bitwise_not_tosa_INT(test_data: Tuple):
57+
pipeline = TosaPipelineINT[input_t1](
58+
BitwiseNot(),
59+
(test_data,),
60+
aten_op=aten_op,
61+
exir_op=exir_op,
62+
)
63+
pipeline.pop_stage("quantize")
64+
pipeline.pop_stage("check.quant_nodes")
65+
pipeline.run()
66+
67+
68+
@common.parametrize("test_data", test_data_suite)
69+
def test_bitwise_not_u55_INT(test_data: Tuple):
70+
# We don't delegate bitwise_not since it is not supported on U55.
71+
pipeline = OpNotSupportedPipeline[input_t1](
72+
BitwiseNot(),
73+
(test_data,),
74+
{exir_op: 1},
75+
quantize=True,
76+
u55_subset=True,
77+
)
78+
pipeline.run()
79+
80+
81+
@common.XfailIfNoCorstone320
82+
@common.parametrize("test_data", test_data_suite)
83+
def test_bitwise_not_u85_INT(test_data: Tuple):
84+
pipeline = EthosU85PipelineINT[input_t1](
85+
BitwiseNot(),
86+
(test_data,),
87+
aten_ops=aten_op,
88+
exir_ops=exir_op,
89+
)
90+
pipeline.pop_stage("quantize")
91+
pipeline.pop_stage("check.quant_nodes")
92+
pipeline.run()
93+
94+
95+
@common.parametrize("test_data", test_data_suite)
96+
@common.SkipIfNoModelConverter
97+
def test_bitwise_not_vgf_FP(test_data: Tuple):
98+
# We don't delegate bitwise_not since it is not supported on the FP profile.
99+
pipeline = OpNotSupportedPipeline[input_t1](
100+
BitwiseNot(),
101+
(test_data,),
102+
{exir_op: 1},
103+
quantize=False,
104+
)
105+
pipeline.run()
106+
107+
108+
@common.parametrize("test_data", test_data_suite)
109+
@common.SkipIfNoModelConverter
110+
def test_bitwise_not_vgf_INT(test_data: Tuple):
111+
pipeline = VgfPipeline[input_t1](
112+
BitwiseNot(),
113+
(test_data,),
114+
aten_op,
115+
exir_op,
116+
tosa_version="TOSA-1.0+INT",
117+
)
118+
pipeline.pop_stage("quantize")
119+
pipeline.pop_stage("check.quant_nodes")
120+
pipeline.run()

0 commit comments

Comments
 (0)