Skip to content

Commit e485840

Browse files
Arm backend: Support bool inputs for aten.bitwise_not via logical_not (pytorch#16417)
TOSA bitwise_not does not accept boolean inputs. For boolean tensors, aten.bitwise_not is equivalent to aten.logical_not, so rewrite to enable lowering. Signed-off-by: Yufeng Shi <yufeng.shi@arm.com>
1 parent 93717b2 commit e485840

4 files changed

Lines changed: 49 additions & 0 deletions

File tree

‎backends/arm/_passes/__init__.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@
113113
from .replace_scalar_with_tensor_pass import ( # noqa
114114
ReplaceScalarWithTensorByProfilePass,
115115
)
116+
from .rewrite_bool_bitwise_not_to_logical_not_pass import ( # noqa
117+
RewriteBoolBitwiseNotToLogicalNotPass,
118+
)
116119
from .rewrite_bool_to_fp32_cast_via_int8_pass import ( # noqa
117120
RewriteBoolToFp32CastViaInt8Pass,
118121
)

‎backends/arm/_passes/arm_pass_manager.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
RemoveNoopPass,
104104
ReplaceInfAndLimitValuesPass,
105105
ReplaceScalarWithTensorByProfilePass,
106+
RewriteBoolBitwiseNotToLogicalNotPass,
106107
RewriteBoolToFp32CastViaInt8Pass,
107108
RewriteConvPass,
108109
RewriteMatmulPass,
@@ -222,6 +223,7 @@ def _tosa_pipeline(
222223
self.add_passes(
223224
[
224225
FuseQuantizedActivationPass(),
226+
RewriteBoolBitwiseNotToLogicalNotPass(),
225227
RewriteBoolToFp32CastViaInt8Pass(),
226228
ConvertToClampPass(),
227229
DecomposeTOSAUnsupportedClampPass(),
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
7+
from typing import Set, Type
8+
9+
import torch
10+
from executorch.backends.arm._passes import ArmPass
11+
from executorch.exir.dialects._ops import ops as exir_ops
12+
from executorch.exir.pass_base import ExportPass
13+
14+
15+
class RewriteBoolBitwiseNotToLogicalNotPass(ArmPass):
16+
"""
17+
Rewrites ``aten.bitwise_not`` on boolean tensors to ``aten.logical_not``.
18+
19+
TOSA ``bitwise_not`` does not support boolean inputs. On boolean tensors,
20+
``bitwise_not`` is equivalent to ``logical_not``, so this rewrite preserves
21+
semantics while enabling lowering.
22+
"""
23+
24+
_passes_required_after: Set[Type[ExportPass]] = set()
25+
26+
_TARGET_OPS = {
27+
exir_ops.edge.aten.bitwise_not.default,
28+
}
29+
30+
def call_operator(self, op, args, kwargs, meta):
31+
if op not in self._TARGET_OPS:
32+
return super().call_operator(op, args, kwargs, meta)
33+
34+
if meta["val"].dtype == torch.bool:
35+
x = args[0]
36+
return super().call_operator(
37+
exir_ops.edge.aten.logical_not.default,
38+
(x,),
39+
{},
40+
meta,
41+
)
42+
43+
return super().call_operator(op, args, kwargs, meta)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"pattern2_int8": 0xCC * torch.ones(1, 2, 2, 2, dtype=torch.int8),
3030
"pattern2_int16": 0xCCCC * torch.ones(1, 2, 2, 2, dtype=torch.int16),
3131
"pattern2_int32": 0xCCCCCCCC * torch.ones(1, 2, 2, 2, dtype=torch.int32),
32+
"pattern_bool": torch.tensor([True, False, True], dtype=torch.bool),
3233
"rand_rank2": torch.randint(-128, 127, (10, 10), dtype=torch.int8),
3334
"rand_rank4": torch.randint(-128, 127, (1, 10, 10, 10), dtype=torch.int8),
3435
}

0 commit comments

Comments
 (0)